在Rails应用程序中,我的应用程序控制器中有两个方法。其中一个对用户进行身份验证(即检查用户是否已登录,如果没有则将其重定向到登录)。另一个执行该任务的前半部分(即检查用户是否已登录)。
检查完成后,如果用户已登录,则这两种方法都应该使用@current_user
为当前登录的用户的用户对象设置User.find
变量。
前者:
protected
def authenticate_user
if session[:user_id]
# set current user object to @current_user object variable
@current_user = User.find session[:user_id]
return true
else
flash[:notice] = "You must log in first."
flash[:color] = "invalid"
redirect_to(:controller => 'sessions', :action => 'login')
return false
end
end
后者:
def check_login_status
if session[:user_id]
@current_user = User.find session[:user_id]
return true
end
end
如您所见,每种方法的前半部分的逻辑是相同的。但是,authenticate_user
正确设置了@current_user
变量; check_login_status
根本没有设置它(例如,布局文件中的检查显示为@current_user.nil? == true
。
这是布局文件的相关部分:
<% if not @current_user.nil? %>
Logged in as <%= @current_user.username %> —
<a href="/logout">log out</a> —
<a href="/dashboard">dashboard</a> —
<a href="/contacts">contacts</a> —
<a href="/help">help</a>
<% if @current_user.is_admin %>
— <a href="/admin">admin</a>
<% end %>
<% else %>
<a href="/login">log in</a> —
<a href="/sign-up">sign up</a> —
<a href="/help">help</a>
<% end %>
我显示了第二组链接,表明我已退出。
那么,为什么后者没有正确设置@current_user
变量呢?它是否与protected
标记有关(虽然不知怎的,我怀疑它)?
答案 0 :(得分:1)
我想说最可能的解释是check_login_status
根本没有被调用,因为两者在逻辑上是等价的。但是两者都重复相同的认证逻辑!
如果你坚持重新发明授权轮(除非是为了学习目的),你应该避免在你的控制器和视图中传播认证逻辑。
而是使用帮助程序模块创建一个简单的API进行身份验证。该模块应该是应用程序中唯一知道用户如何存储在会话中的部分:
module AuthorizationHelper
def current_user
return nil unless session[:user_id]
# conditional assignment so DB is only queried once!
@current_user ||= User.find(session[:user_id])
end
def sign_in!(user)
reset_session
session[:user_id] = user.id
@current_user = user
end
def sign_out!(user)
reset_session
@current_user = nil
end
def signed_in?
current_user.present?
end
end
现在我们只在ApplicationController
中包含帮助器。
class ApplicationController
include AuthorizationHelper
# ...
end
我们还希望以可重复和可扩展的方式确保授权。执行此操作的一种好方法是引发异常并使用rescue_from
缓存它。
让我们创建自己的错误类:
class User < ActiveRecord::Base
class AuthorizationError < StandardError; end
end
让我们添加一个授权方法:
module authorizationHelper
# ..
def authorize!
raise User::AuthorizationError unless signed_in?
end
end
现在我们可以在控制器中使用它:
class ThingsController < ApplicationController
before_action :authorize!
end
然而它不是很有用,因为它只是导致应用程序崩溃!让救援异常:
class ApplicationController
include AuthorizationHelper
rescue_from User::AuthorizationError, with: :deny_access
def deny_access
redirect_to(controller: 'sessions', action: 'login') and return
end
end