RoR切换至Aurora写实例处理Devise密码重置时的CSRF标签问题
问题根源
你之前仅在PasswordsController#edit方法内部用ActiveRecord::Base.connected_to指定写实例,但这个作用范围仅覆盖action的业务逻辑,视图渲染(包括布局中的csrf_meta_tags)阶段会切回只读连接,而csrf_meta_tags会触发session写入操作,从而抛出ActiveRecord::ReadOnlyError。
可行解决方案
1. 用around_action包裹整个请求周期
在自定义的PasswordsController中添加around_action,让整个请求(包括action执行、视图渲染)都绑定写实例:
class PasswordsController < Devise::PasswordsController around_action :force_write_connection private def force_write_connection ActiveRecord::Base.connected_to(role: :writing) do yield end end end
从请求进入到响应返回的全流程都会使用写实例,布局渲染时的session操作自然不会触发只读错误。
2. 将CSRF令牌存储改为Cookie模式
修改Rails的CSRF存储策略,避免依赖数据库session:
在config/initializers/csrf_config.rb中添加配置:
# 切换CSRF存储到Cookie,避免写入数据库session Rails.application.config.action_controller.csrf_store = :cookie_store # 根据环境配置Cookie安全属性 Rails.application.config.action_dispatch.cookies_same_site_protection = :lax Rails.application.config.action_dispatch.cookies_secure = Rails.env.production?
这种方式下,csrf_meta_tags生成令牌时直接操作Cookie,无需访问数据库,从根源上避免了只读实例的冲突。
3. 强制Session模型始终使用写实例
如果必须保留数据库session存储,可以给Session模型指定固定连接写实例:
首先创建app/models/session.rb:
class Session < ActiveRecord::SessionStore::Session # 强制Session操作始终走主库(写实例) connects_to database: { writing: :primary, reading: :primary } end
然后修改config/initializers/session_store.rb:
Rails.application.config.session_store :active_record_store, model: 'Session'
这样无论当前请求的数据库连接角色是什么,Session的读写都会强制走写实例,解决csrf_meta_tags触发的session写入报错。
内容的提问来源于stack exchange,提问作者LewlSauce
相关产品推荐
相关产品推荐

