0

我正在开发我的第一个 rails api 服务器。

我的User模型有一个控制器,看起来像这样:

class UsersController < ApplicationController
  def index
    if current_user.admin?
      @users = User.all
      render json: @users
    else
      render json: { message: 'You do not have the appropriate permissions to access this resource' }, status: 401
    end
  end

  def show
    if User.exists?(@id)
      @id = params[:id]
      if current_user.id.to_s == @id || current_user.admin?
        @user = User.find(@id)
        render json: @user
      else
        render json: { message: 'You do not have the appropriate permissions to access this resource' }, status: 401
      end
    else
      render json: { message: 'Requested resource not found' }, status: 404
    end
  end
end

对于这两种控制器方法,我想要并且目前拥有的是:

  • /users仅当发出请求的经过身份验证的用户具有角色时才获取所有用户admin
  • /users/:idid仅当发出请求的经过身份验证的用户具有匹配id或具有角色时才获取用户admin

当前的实现打破了 DRY 理念。原因是处理请求用户是否有权访问所请求资源的逻辑在两个控制器方法中重复。此外,任何模型的控制器方法show都会重复检查请求的资源是否存在的逻辑。我也觉得这种实现方式适用于胖控制器,我宁愿它们是瘦的。

我想从社区和以前解决过这个问题的人那里知道什么;为了符合 DRY 理念并保持控制器瘦身,最好的方法是什么。

很高兴知道:我正在使用devisedevise-token-auth进行身份验证。

4

2 回答 2

2

您需要使用某种授权宝石,例如cancancan. 这正是您所需要的。else也不是elsifelsif后跟条件。

于 2020-06-05T07:06:07.637 回答
1

您可以改用github.com/varvet/pundit进行授权。

它与控制器匹配,而不是将授权放在控制器中,您可以使用它将授权移出到另一个类。

我已经在多个 Rails/Rails-API 项目中使用了它,到目前为止还没有遇到问题。

而不是写上面的代码。您可以改为这样做。

此外,为了可读性,优先考虑早期返回而不是嵌套 if。

在你的控制器中。

class UsersController < ApplicationController
  def index
    authorize User # This will call the policy that matches this controller since this is UsersController it will call `UserPolicy`

    @users = User.all

    render :json => @users
  end

  def show
    @user = User.find_by :id => params[:id] # Instead of using exists which query the data from db then finding it again, you can use find_by which will return nil if no records found.

    if @user.blank?
      return render :json => {:message => 'User not found.'}, :status => 404
    end

    authorize @user # This will call the policy that matches this controller since this is UsersController it will call `UserPolicy`

    render :json => @user
  end
end

在您的保单中

class UserPolicy < ApplicationPolicy
  def index?
    @user.admin? # The policy is called in controller then this will check if the user is admin if not it will raise Pundit::NotAuthorizedError
  end

  def show?
    @user.admin? || @record == @user # The policy is called in controller then this will check if the user is admin or the user is the same as the record he is accessing if not it will raise Pundit::NotAuthorizedError
  end
end

在你的 ApplicationController

class ApplicationController < ActionController::API
  include Pundit

  rescue_from Pundit::NotAuthorizedError, :with => :show_forbidden



  private

  def show_forbidden exception
    return render :json => {
      :message => 'You are not authorized to perform this action.'
    }, :status => 403
  end
end
于 2020-06-05T07:08:09.383 回答