环境:
casServer 5.2.6
pac4j-cas 3.0.2
buji-pac4j 4.0.0
shiro 1.9.1 (ruoyi不分离版)

  1. 集成
    先按照这个帖子集成
    客户端配置成IP地址 image.png
    启动两个客户端,在直接写IP地址的情况下就可以实现单点登录和单点登出。
  2. 域名单点登出
    按照上面配置好,把cas服务和客户端地址改成域名,就会发现服务A退出登录,服务B却依然在线,只能等ST过期后才会回到CAS服务登录页面。(不清楚用IP的为什么是可以的,有知道的大佬可以解惑下吗)
    2.1 修改配置
    重写DefaultCasLogoutHandler
1
import org.pac4j.core.context.WebContext; import org.pac4j.core.context.session.SessionStore; import org.pac4j.cas.logout.DefaultCasLogoutHandler; public class CusLogoutHandler<C extends WebContext> extends DefaultCasLogoutHandler<C> { @Override public void destroySessionBack(final C context, final String ticket) { final Object trackableSession = getStore().get(ticket); logger.debug("ticket: {} -> trackableSession: {}", ticket, trackableSession); if (trackableSession == null) { logger.error("No trackable session found for back channel logout. Either the session store does not support to track session " + "or it has expired from the store and the store settings must be updated (expired data)"); } else { getStore().remove(ticket); // renew context with the original session store final SessionStore sessionStore = context.getSessionStore(); if (sessionStore == null) { logger.error("No session store available for this web context"); } else { // newSessionStore就是登录进来保存的session // 具体可以看org.pac4j.cas.logout.DefaultCasLogoutHandler#recordSession final SessionStore<C> newSessionStore = sessionStore.buildFromTrackableSession(context,trackableSession); if (newSessionStore != null) { logger.debug("newSesionStore: {}", newSessionStore); // 主要是这里 得获取本次票据ST对应的Shiro的session final String sessionId = ((CusShiroSessionStore)newSessionStore).getSessionId(); logger.debug("remove sessionId: {}", sessionId); getStore().remove(sessionId); destroy(context, newSessionStore, "back"); } else { logger.error("The session store should be able to build a new session store from the tracked session"); } } } } }

重写ShiroSessionStore

1
import io.buji.pac4j.context.ShiroSessionStore; import org.apache.shiro.session.Session; import org.pac4j.core.context.J2EContext; import org.pac4j.core.context.session.SessionStore; public class CusShiroSessionStore extends ShiroSessionStore { Session session; public CusShiroSessionStore(Session session) { this.session = session; } public CusShiroSessionStore() { } @Override public Object getTrackableSession(J2EContext context) { return getSession(false); } @Override public SessionStore<J2EContext> buildFromTrackableSession(final J2EContext context, final Object trackableSession) { // 保存ST对应的session if (trackableSession != null) { return new CusShiroSessionStore((Session)trackableSession); } else { return null; } } @Override public String getOrCreateSessionId(J2EContext context) { return super.getOrCreateSessionId(context); } public String getSessionId() { // 当cas退出时会通知客户端,获取缓存中的session并移除 return session.getId().toString(); } @Override public boolean destroySession(final J2EContext context) { if (session != null) { try { session.stop(); }catch (Exception e){ } } return true; } }

修改Pac4jConfig

1
/** * 自定义存储 * * @return */ @Bean public ShiroSessionStore shiroSessionStore() { return new CusShiroSessionStore(); } @Bean public CasConfiguration casConfig() { final CasConfiguration configuration = new CasConfiguration(); //CAS server登录地址 configuration.setLoginUrl(casServerUrl + "/login"); //CAS 版本,默认为 CAS30,我们使用的是 CAS20 configuration.setProtocol(CasProtocol.CAS20); configuration.setAcceptAnyProxy(true); configuration.setPrefixUrl(casServerUrl + "/"); CusLogoutHandler<J2EContext> cusLogoutHandler = new CusLogoutHandler<>(); // 设置是否销毁缓存中的session,其实就是调用缓存的session对象的destroySession方法 // org.pac4j.cas.logout.DefaultCasLogoutHandler#destroy cusLogoutHandler.setDestroySession(true); configuration.setLogoutHandler(cusLogoutHandler); return configuration; }

到这里就可以实现在域名的情况下单点退出了
2.2 io.buji.pac4j.filter.SecurityFilter验证是否需要重新到cas登录
在org.pac4j.core.engine.DefaultSecurityLogic#perform中

image.png 会获取一个profiles,这个profiles就是当前session里存放的本客户端信息(org.pac4j.cas.client.CasClient或继承类)
在执行到后面当profiels为空时才会跳转到cas登录页面

image.png

2.3 存在问题
当重写过后客户端,退出来然后在重新登录进来,偶尔会出现请求cas服务端返回401状态,但是只要刷新一下页面重新请求下cas服务端就就没有问题了,相当于是又重新在casRealm里面登录了一次。很奇怪,当然改了之后用IP也会有这种情况。