从零开始理解 Rails 权限控制
在搭建一个多人使用的网站时,权限管理几乎是绕不开的环节。比如你做了一个后台系统,普通员工只能查看订单,管理员才能删除用户。这种“谁可以做什么”的规则,就是权限控制的核心。
常见的权限场景
假设你在开发一个内容管理系统,编辑能写文章,审核员能发布,管理员还能删文章。如果所有人都能删,那系统迟早出乱子。所以必须给不同角色设置不同的操作权限。
用 CanCanCan 实现权限逻辑
Rails 社区里最流行的权限库是 CanCanCan,它是 CanCan 的延续版本,维护活跃,用起来也顺手。先在 Gemfile 里加上:
gem 'cancancan', '~> 3.4'
然后执行 bundle install 安装。接着生成一个 Ability 类,用来定义所有权限规则:
rails g cancan:ability
打开生成的 app/models/ability.rb 文件,写入角色判断逻辑:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # 防止未登录用户为 nil
if user.admin?
can :manage, :all
elsif user.editor?
can :read, Article
can :create, Article
can :update, Article
elsif user.reviewer?
can :read, Article
can :publish, Article
else
can :read, Article
end
end
end
控制器中启用权限检查
在需要控制权限的控制器里,加上 load_and_authorize_resource,它会自动根据当前用户和 Ability 中的规则判断是否允许访问。
class ArticlesController < ApplicationController
load_and_authorize_resource
def index
@articles = Article.all
end
def destroy
@article.destroy
redirect_to articles_path
end
end
这时候如果普通用户尝试访问删除接口,框架会自动抛出 AccessDenied 异常。你可以在 ApplicationController 里统一处理:
class ApplicationController < ActionController::Base
rescue_from CanCan::AccessDenied do |exception|
redirect_to root_path, alert: '权限不足,无法执行此操作'
end
end
视图中隐藏不可操作项
除了后端拦截,前端也该配合隐藏按钮。比如只有管理员才显示“删除”链接:
<% if can? :destroy, @article %>
<= link_to '删除', @article, method: :delete, confirm: '确认删除?' %>
<% end %>
这样用户体验更自然,不会看到点不了的按钮。
自定义动作也要纳入管理
像“发布文章”这种非标准 CRUD 操作,需要在 Ability 中显式声明:
can :publish, Article if user.reviewer?
然后在控制器中手动检查:
def publish
@article = Article.find(params[:id])
authorize! :publish, @article
@article.update(published: true)
redirect_to @article
end
调试权限问题的小技巧
当发现某个权限没生效,可以在控制台快速验证:
user = User.find(1)
ability = Ability.new(user)
ability.can?(:destroy, Article.first) # => false
这个方法比反复刷新页面测试快得多。
权限控制不是一次性工作,随着业务变复杂,规则也会变多。建议把 Ability 类拆分成多个模块,按角色或资源分类维护,避免变成一坨大泥球。