如何为Active Record Session Store添加字段以实现CAS单点登出
嘿,我之前在做Devise+CAS的Rails应用时,也卡在单点登出这个环节——毕竟要关联CAS ticket和Rails会话才能精准销毁对应会话。下面是我踩坑后整理的完整解决方案,针对Active Record Session Store的场景:
1. 给会话表添加CAS Ticket字段
首先得让会话表能存储CAS ticket,先生成迁移文件:
rails generate migration AddCasTicketToSessions cas_ticket:string:index rails db:migrate
然后扩展默认的会话模型,方便后续通过ticket查找会话。在config/initializers/session_store.rb里添加:
Rails.application.config.session_store :active_record_store, key: '_your_app_session' # 扩展会话模型,新增ticket查询方法 class ActiveRecord::SessionStore::Session def self.find_by_cas_ticket(ticket) find_by(cas_ticket: ticket) end end
2. 登录成功时把CAS Ticket存入会话
用户通过CAS认证成功后,要把返回的ticket绑定到当前会话。可以利用Devise的回调来实现,比如在User模型里:
# app/models/user.rb devise :cas_authenticatable def after_cas_authenticate(authentication) # 获取CAS返回的ticket(具体键名根据你用的CAS gem调整,比如有的是authentication.ticket) cas_ticket = authentication.ticket # 找到当前会话记录并更新ticket字段 if session_record = ActiveRecord::SessionStore::Session.find_by(session_id: request.session_options[:id]) session_record.update(cas_ticket: cas_ticket) end end
如果你用的是自定义的会话控制器,也可以在登录动作完成后处理:
# app/controllers/users/sessions_controller.rb class Users::SessionsController < Devise::SessionsController def create super if current_user # 从请求环境中获取CAS ticket,具体键名看gem文档 cas_ticket = request.env['cas.last_ticket'] session_record = ActiveRecord::SessionStore::Session.find_by(session_id: session.id) session_record.update(cas_ticket: cas_ticket) if session_record end end end
3. 处理CAS单点登出请求
CAS服务器会发送POST请求到你的回调地址,我们需要解析ticket并销毁对应会话:
首先添加路由:
# config/routes.rb post '/cas/logout', to: 'cas#logout'
然后创建CAS控制器:
# app/controllers/cas_controller.rb class CasController < ApplicationController skip_before_action :verify_authenticity_token # CAS请求不带CSRF token,需跳过验证 def logout # 注意:不同CAS服务器的参数格式可能不同,有的是直接传ticket,有的是XML格式的logoutRequest # 这里先处理直接传ticket的情况,XML格式的处理看下方注释 cas_ticket = params[:ticket] # 若CAS发送的是XML请求,用下面的代码解析ticket # require 'nokogiri' # logout_request = Nokogiri::XML(request.body.read) # cas_ticket = logout_request.at_xpath('//samlp:SessionIndex')&.content if cas_ticket # 找到对应会话并销毁 session_record = ActiveRecord::SessionStore::Session.find_by_cas_ticket(cas_ticket) session_record.destroy if session_record end # 返回CAS服务器要求的响应,通常200 OK即可 head :ok end end
记得在CAS服务器后台配置单点登出的回调地址为你的/cas/logout。
4. 额外注意事项
- 多服务器部署时,要确保所有应用节点共享同一个会话数据库,不然单点登出无法跨节点生效。
- 测试时可以手动调用
/cas/logout接口,传入已保存的ticket,验证会话是否被销毁。 - 部分CAS gem(比如devise_cas_authenticatable)可能自带部分单点登出支持,建议先查看官方文档确认是否有更简便的配置方式。
内容的提问来源于stack exchange,提问作者Juan Fuentes
相关产品推荐
相关产品推荐

