智享技巧屋
第二套高阶模板 · 更大气的阅读体验

Ruby on Rails权限控制实战指南(实战经验分享)

发布时间:2025-12-15 21:33:36 阅读:320 次

从零开始理解 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 类拆分成多个模块,按角色或资源分类维护,避免变成一坨大泥球。