You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 04:16:10