为什么两个逻辑上相同的方法不执行相同的任务



在 Rails 应用程序中,我的应用程序控制器中有两个方法。其中一个对用户进行身份验证(即检查用户是否已登录,如果没有,则重定向他们登录)。另一个执行该任务的前半部分(即检查用户是否已登录)。

检查完成后,如果用户已登录,这两种方法都应该使用 User.find 为当前登录的用户的用户对象设置一个 @current_user 变量。

前者:

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标记有什么关系吗(尽管我不知何故对此表示怀疑)?

我想

说最可能的解释是根本没有调用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

最新更新