如何在Ruby on Rails应用层面实现全路由加密
Rails应用全局路由加密实现方案
实现核心是在不修改原有业务代码的前提下,新增两层统一逻辑:加密路由生成逻辑、加密路由解析转发逻辑,全程兼容原有link_to、redirect_to、路由辅助方法的调用习惯。
步骤1:初始化路由加密工具
使用Rails内置的加密模块实现,无需额外引入第三方gem,在config/initializers目录下新建route_encryptor.rb:
# config/initializers/route_encryptor.rb # 基于Rails自带密钥生成加密器,加密强度满足业务需求 ROUTE_ENCRYPTOR = ActiveSupport::MessageEncryptor.new( Rails.application.secret_key_base.byteslice(0..31), cipher: 'aes-256-gcm' ) # 路径加密方法:原始路径段转URL安全的加密串 def encrypt_route_segments(segments) raw_encrypted = ROUTE_ENCRYPTOR.encrypt_and_sign(segments.join('/')) raw_encrypted.gsub('+', '-').gsub('/', '_').gsub('=', '') end # 路径解密方法:加密串还原为原始路径段,解密失败返回nil def decrypt_route_segments(encrypted_str) normalized_str = encrypted_str.gsub('-', '+').gsub('_', '/') normalized_str += '=' * ((4 - normalized_str.length % 4) % 4) ROUTE_ENCRYPTOR.decrypt_and_verify(normalized_str).split('/') rescue ActiveSupport::MessageEncryptor::InvalidMessage nil end
步骤2:配置全局路由解析规则
修改config/routes.rb,新增加密路由的全局匹配转发逻辑,原有业务路由无需任何修改:
# config/routes.rb Rails.application.routes.draw do # 加密路由匹配约束 class EncryptedRouteConstraint def matches?(request) @decrypted_segments = decrypt_route_segments(request.path_info.delete_prefix('/')) @decrypted_segments.present? end def decrypted_segments @decrypted_segments end end # 加密路由优先级最高,捕获所有加密路径后转发到对应原始路由 match '*encrypted_path', to: -> (env) { request = ActionDispatch::Request.new(env) constraint = EncryptedRouteConstraint.new if constraint.matches?(request) env['PATH_INFO'] = '/' + constraint.decrypted_segments.join('/') Rails.application.routes.router.serve(env) else [404, { 'Content-Type' => 'text/plain' }, ['Not Found']] end }, via: :all # ------------------- 以下是原有业务路由,无需修改 ------------------- resources :users # 其他原有路由... end
步骤3:覆写路由生成逻辑,自动加密输出URL
在config/initializers目录下新建route_url_override.rb,保证所有路由辅助方法自动输出加密后的路径:
# config/initializers/route_url_override.rb module EncryptedRouteHelper def url_for(options = nil) original_url = super(options) # 跳过非本应用域名URL、静态资源路径、系统内置路径 return original_url if original_url.start_with?('http') && !original_url.start_with?(root_url) return original_url if original_url.start_with?('/assets/', '/rails/active_storage/', '/rails/info/') uri = URI.parse(original_url) return original_url if uri.path == '/' || uri.path.blank? # 加密路径段,保留原有query参数和锚点 encrypted_path = encrypt_route_segments(uri.path.delete_prefix('/').split('/')) new_path = "/#{encrypted_path}" new_path += "?#{uri.query}" if uri.query.present? new_path += "##{uri.fragment}" if uri.fragment.present? original_url.start_with?('http') ? URI.join(root_url, new_path).to_s : new_path rescue URI::InvalidURIError original_url end end # 把加密逻辑混入所有需要生成URL的场景 ActionView::Base.include EncryptedRouteHelper ActionController::Base.include EncryptedRouteHelper ActiveJob::Base.include EncryptedRouteHelper if defined?(ActiveJob)
注意事项
- 可根据业务需求调整跳过加密的路径规则,比如后台管理路径、开放API路径可单独配置不加密
- 更换加密密钥前需要做好旧密钥兼容逻辑,避免存量URL直接失效
- 加密后的字符串默认是URL安全格式,不会出现特殊字符导致路由匹配失败的问题
内容的提问来源于stack exchange,提问作者nirav kanani
相关产品推荐
相关产品推荐

