Rails 5子域名权限控制问题:限制用户访问合法子域名
Rails 5子域名权限控制完整实现方案
嘿,我来帮你把这个子域名权限控制的逻辑补全,刚好我之前做过类似的需求,给你一套实用的实现方案:
首先,我们要在ApplicationController里完成那个验证方法,还要处理好循环重定向的问题(比如用户访问Account创建页时不能再验证子域名),另外还要考虑已登录用户的权限校验,避免他们跨子域名访问不属于自己的账户。
第一步:完善ApplicationController的验证逻辑
把你没写完的方法补全,加上前置钩子和必要的判断:
class ApplicationController < ActionController::Base protect_from_forgery with: :exception # 给所有控制器动作添加子域名验证,除了我们指定跳过的 before_action :validate_subdomain private def validate_subdomain # 跳过不需要验证的页面,比如账户创建页、登录页,防止循环重定向 return if skip_subdomain_validation? # 获取当前请求的子域名,处理空值情况 current_subdomain = request.subdomain.presence # 如果没有子域名(访问主域名),直接跳转到账户创建页 if current_subdomain.nil? redirect_to new_account_path, alert: "请先创建或绑定您的账户" return end # 根据子域名查找对应的Account @current_account = Account.find_by(subdomain: current_subdomain) # 子域名不存在?直接跳转到创建页 if @current_account.nil? redirect_to new_account_path, alert: "该子域名对应的账户不存在,请先创建账户" return end # 额外校验:已登录用户必须属于当前子域名的Account,防止跨域访问 if user_signed_in? && !current_user.account_ids.include?(@current_account.id) sign_out current_user redirect_to new_user_session_path, alert: "您无权访问该子域名,请切换到对应账户的子域名登录" end end # 定义需要跳过验证的控制器和动作 def skip_subdomain_validation? # 账户创建相关动作、Devise登录登出动作都跳过 (controller_name == "accounts" && %w[new create].include?(action_name)) || (controller_name == "sessions" && %w[new create destroy].include?(action_name)) end end
第二步:给Account模型添加子域名唯一性验证
确保每个子域名只能对应一个账户,在Account模型里加验证:
class Account < ApplicationRecord has_many :users validates :subdomain, presence: true, uniqueness: { case_sensitive: false } end
第三步:路由层面做子域名约束
在routes.rb里区分子域名路由和主域名路由,让逻辑更清晰:
Rails.application.routes.draw do # 所有需要子域名验证的功能放在这个约束块里 constraints(subdomain: /.+/) do root 'dashboard#index' # 子域名下的首页,比如仪表盘 # 其他需要子域名的路由... end # 主域名下的公共路由:账户创建、用户登录 root 'home#index' resources :accounts, only: [:new, :create] devise_for :users end
第四步:优化登录逻辑(如果用Devise)
如果你的项目用Devise做用户认证,最好在登录时就校验用户和子域名的对应关系,避免用户登录后再被踢出去:
先自定义Devise的Sessions控制器:
class Users::SessionsController < Devise::SessionsController def create user = User.find_by(email: params[:user][:email]) # 如果用户存在,但子域名不匹配,直接拒绝登录 if user && user.account.subdomain != request.subdomain flash[:alert] = "您的账户对应子域名不符,请切换到正确的子域名登录" redirect_to new_user_session_path else # 正常走Devise的登录流程 super end end end
然后在routes.rb里指定用自定义的控制器:
devise_for :users, controllers: { sessions: "users/sessions" }
开发环境测试小技巧
要在本地测试子域名,需要修改你的hosts文件,添加几条记录:
127.0.0.1 lvh.me 127.0.0.1 test.lvh.me 127.0.0.1 demo.lvh.me
这样访问test.lvh.me:3000就能模拟test子域名的请求了。
内容的提问来源于stack exchange,提问作者Shawn Wilson
相关产品推荐
相关产品推荐

