rails tutorials: sessions

HTTP 协议没有状态,每个请求都是独立的事务,无法使用之前请求中的信息。所以,在 HTTP 协议中无法 在两个页面之间记住用户的身份。需要用户登录的应用必须使用会话(session)。会话是两台电脑(例如运 行 Web 浏览器的客户端电脑和运行 Rails 的服务器)之间的半永久性连接。在 Rails 中实现会话最常见的方式是使用 cookie。cookie 是存储在用户浏览器中的少量文本。访问其他页面时,cookie 中存储的信息仍在,所以可以在 cookie 中存储一些信息,例如用户的 ID,让应用从数据库中检索 已登录的用户。这一节使用 Rails 提供的 session 方法实现临时会话,浏览器关闭后会话自动失效。

把会话看成 REST 式资源便于操作,访问登录页面时渲染一个用于创建会话的表单,登录时创建会话,退出 时再把会话销毁。

创建 sessions 控制器

1
rails generate controller Sessions new

获得会话的标准 REST 式动作

config/routes.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Rails.application.routes.draw do

root "todo_lists#index"

# # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
# resources :todo_lists do
# resources :todo_items
# end

get '/login', to: 'sessions#new'
post '/login', to: 'sessions#create'
delete "/logout",to: "sessions#destroy"

end

创建登录表单

app/views/sessions/new.html.erb

1
2
3
4
5
6
7
8
9
10
11
12
13
<h1>Login</h1>
<%= form_with(url: login_path, scope: :session, local: true) do |f| %>
<div class="field">
<%= f.label :username %> <br/> <%= f.text_field :username %>
</div>
<p/>
<div class="field">
<%= f.label :password %> <br/>
<%= f.password_field :password %> </div>
<p/>
<div class="actions">
<%= f.submit "Login" %> </div>
<% end %>

编写控制器

app/controllers/application_controller.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class ApplicationController < ActionController::Base
before_action :ensure_login
helper_method :logged_in?, :current_user

protected

def ensure_login
redirect_to login_path unless logged_in?
end

def logged_in?
session[:user_id] # nil is false
end

def current_user
@current_user ||= User.find(session[:user_id])
end
end

app/controllers/sessions_controller.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class SessionsController < ApplicationController

skip_before_action :ensure_login, only: [:new, :create]


def new
end

def create
user = User.find_by(username: params[:session][:username])
if user && user.authenticate(params[:session][:password])
session[:user_id] = user.id
redirect_to root_path, notice: "Logged in successfully"
else
flash.now[:danger] = 'Invalid username/password combination'
redirect_to login_path, alert: "Invalid username/password combination"
end
end

def destroy
reset_session
redirect_to login_path, notice: "You have been logged out"
end

end

修改其他布局

1
2
3
4
5
<% if logged_in? %>
# 登录用户看到的链接
<% else %>
# 未登录用户看到的链接
<% end %>
Donate article here