CAS单点登出,调整CAS源码,实现前后端分离单点登出、清除redis、shiro登录状态
前端點擊“登出”按鈕,跳轉到CAS的登出。
CAS默認配置了單點登出,在登出后,會向所有客戶端系統發送這個用戶登出的報文。
各客戶端系統有責任接收并處理這個用戶登出的報文,然后在注銷該用戶會話在本客戶端的信息。
若不進行 #CAS配置客戶端地址 或 #客戶端后端 ,則網頁里的登出按鈕點擊之后就無法通知其他客戶端系統登出。
目前使用了客戶端集成CAS源碼并修改的方法,來對客戶端做了Filter接收CAS登出請求之后的處理。
有悖于CAS的建議"不要修改CAS源碼"。待以后再研究看有沒有什么優化方法。
CAS配置客戶端地址
??CAS的單點登出是默認啟用的。
??CAS向客戶端發送地址,默認為客戶端請求的URL。但該請求的URL是客戶端的前端地址(含端口),CAS需要向客戶端的后端發送請求。因此需要對對CAS配置客戶端的指定地址。
??CAS 對客戶端信息的存儲在/services/目錄下,這里配置jl-iam-client的信息,文件命名為iam-client-10000003.json,內容如下。
??其中
- @class - 按默認填寫
- serviceId - 正則表達式匹配客戶端請求到CAS的來源URL,這里是前端的地址。因為請求都是從前端window.location.href過來的。
- name - 客戶端名稱
- id - ID,不要重復
- description - 描述
- evalutionOrder - 順序。一個客戶端的URL匹配成功了多個客戶端信息,以evalutionOrder最小的來處理。
- logoutType - 登出類型??商?#34;BACK_CHANNEL"或"FRONT_CHANNEL",分別對應后端方式、前端方式。以不配置此項,則默認是"BACK_CHANNEL"。這里使用后端方式登出。
- logoutUrl - 登出URL。如果不配置此項,CAS會向來源URL發送登出請求。在前后端分離的結構下,來源是前端地址,與后端地址不同。因此需要配置該項為后端地址
該地址最后為上下文根+“/”。然后不需再有其他字符。
上下文根一定要有!
上下文根后的"/"一定要有!
否則無法訪問到客戶端的Filter。另外,由于配置單點登出的Filter過濾URL樣式為"/*",因此后面不需要加其他字符。
客戶端后端
配置Filter
??客戶端(jl-iam-client)需要配置Filter來接收CAS單點登出的請求。
??接收請求后需要做這些處理:記錄登出日志、刪除Redis信息、調用Shiro的logout。
??在cas-client-core中的SingleSignOutFilter清空了Session,但是并沒有其他處理。
??由于目前還沒有找到追增處理的方法,因此現在將cas-client-core的SingleSignOutFilter以及SingleSignOutHandler重寫在工程里,對登出處理做了一些定制。
??客戶端使用springboot構建。使用@WebFilter 注解,將定制的SingleSignOutFilter注冊為Filter。
注意注解的參數配置。
@WebFilter(urlPatterns = "/*", initParams = {@WebInitParam(name = "casServerUrlPrefix", value = "https://cas.example.com:8443/cas") }) public class SingleSignOutFilter extends AbstractConfigurationFilter {設置回調方法
??注銷時刪除Session的方法,是在SingleSignOutHandler的destroySession()方法中執行的,并且CAS的票據ticket也是在該方法中解析到的。
??在SingleSignOutHandler中,設置一個回調方法,在destroySession()方法中調用。
回調方法定義
額外登出策略類 ExtraLogoutStrategy.java
public interface ExtraLogoutStrategy {/*** 登出方法* @param ticket CAS票據*/void logout(String ticket); }額外登出策略實現類 CasLogoutStrategy.java
public class CasLogoutStrategy implements ExtraLogoutStrategy {@Autowiredprivate RedisUtil redisUtil;@Autowiredprivate ISysBaseAPI sysBaseAPI;@Resourceprivate BaseCommonService baseCommonService;@Overridepublic void logout(String ticket) {String token = redisUtil.get(CommonConstant.PRIFIX_CAS_TICKET + ticket).toString();String username = JwtUtil.getUsername(token);LoginUser sysUser = sysBaseAPI.getUserByName(username);if(sysUser != null) {baseCommonService.addLog("用戶名: "+sysUser.getRealname()+",退出成功!", CommonConstant.LOG_TYPE_1, null,sysUser);log.info(" 用戶名: "+sysUser.getRealname()+",退出成功!");//清空用戶登錄CAS ticket緩存redisUtil.del(CommonConstant.PRIFIX_CAS_TICKET + ticket);//清空用戶登錄Token緩存redisUtil.del(CommonConstant.PREFIX_USER_TOKEN + token);//清空用戶登錄Shiro權限緩存redisUtil.del(CommonConstant.PREFIX_USER_SHIRO_CACHE + sysUser.getId());//清空用戶的緩存信息(包括部門信息),例如sys:cache:user::<username>redisUtil.del(String.format("%s::%s", CacheConstant.SYS_USERS_CACHE, sysUser.getUsername()));//調用shiro的logoutSecurityUtils.getSubject().logout();}} }調整CAS源碼 SingleSignOutHandler.java
/** 額外登出處理策略回調類 **/private ExtraLogoutStrategy extraLogoutStrategy; /*** 設置額外登出策略* @param extraLogoutStrategy 額外登出策略*/public void setExtraLogoutStrategy(ExtraLogoutStrategy extraLogoutStrategy) {this.extraLogoutStrategy = extraLogoutStrategy;}回調方法設置
修改部分 調整CAS源碼 SingleSignOutFilter.java
@WebFilter(urlPatterns = "/*", initParams = {@WebInitParam(name = "casServerUrlPrefix", value = "https://iamlocal.90tech.cn:8443/cas") }) @Qualifier("casLogoutStrategy")@Autowiredprivate ExtraLogoutStrategy casLogoutStrategy; HANDLER.setExtraLogoutStrategy(casLogoutStrategy);完整類 SingleSignOutFilter.java
@Slf4j @WebFilter(urlPatterns = "/*", initParams = {@WebInitParam(name = "casServerUrlPrefix", value = "https://iamlocal.90tech.cn:8443/cas") }) public class SingleSignOutFilter extends AbstractConfigurationFilter {private static final SingleSignOutHandler HANDLER = new SingleSignOutHandler();private final AtomicBoolean handlerInitialized = new AtomicBoolean(false);@Qualifier("casLogoutStrategy")@Autowiredprivate ExtraLogoutStrategy casLogoutStrategy;@Overridepublic void init(final FilterConfig filterConfig) throws ServletException {log.info("SingleSignOutFilter init.");super.init(filterConfig);if (!isIgnoreInitConfiguration()) {setArtifactParameterName(getString(ConfigurationKeys.ARTIFACT_PARAMETER_NAME));setLogoutParameterName(getString(ConfigurationKeys.LOGOUT_PARAMETER_NAME));setRelayStateParameterName(getString(ConfigurationKeys.RELAY_STATE_PARAMETER_NAME));setLogoutCallbackPath(getString(ConfigurationKeys.LOGOUT_CALLBACK_PATH));HANDLER.setArtifactParameterOverPost(getBoolean(ConfigurationKeys.ARTIFACT_PARAMETER_OVER_POST));HANDLER.setEagerlyCreateSessions(getBoolean(ConfigurationKeys.EAGERLY_CREATE_SESSIONS));HANDLER.setExtraLogoutStrategy(casLogoutStrategy);}HANDLER.init();handlerInitialized.set(true);}回調方法調用
修改部分 調整CAS源碼 SingleSignOutHandler.destroySession()
if (this.extraLogoutStrategy != null) {this.extraLogoutStrategy.logout(token);}完整方法 SingleSignOutHandler.destroySession()
/*** Destroys the current HTTP session for the given CAS logout request.** @param request HTTP request containing a CAS logout message.*/private void destroySession(final HttpServletRequest request) {String logoutMessage = CommonUtils.safeGetParameter(request, this.logoutParameterName, this.safeParameters);if (CommonUtils.isBlank(logoutMessage)) {logger.error("Could not locate logout message of the request from {}", this.logoutParameterName);return;}if (!logoutMessage.contains("SessionIndex")) {logoutMessage = uncompressLogoutMessage(logoutMessage);}logger.trace("Logout request:\n{}", logoutMessage);final String token = XmlUtils.getTextForElement(logoutMessage, "SessionIndex");if (CommonUtils.isNotBlank(token)) {final HttpSession session = this.sessionMappingStorage.removeSessionByMappingId(token);if (session != null) {final String sessionID = session.getId();logger.debug("Invalidating session [{}] for token [{}]", sessionID, token);try {session.invalidate();} catch (final IllegalStateException e) {logger.debug("Error invalidating session.", e);}this.logoutStrategy.logout(request);if (this.extraLogoutStrategy != null) {this.extraLogoutStrategy.logout(token);}}}}CAS ticket緩存
??在CAS代碼處理中,是以接收到的ticket為憑據的。我的系統通過緩存token實現認證。
??所以,為了CAS能與系統成功交互,需要將CAS ticket緩存,并與系統中的token建立關聯。
上文 #額外登出策略實現類 CasLogoutStrategy.java 中,已經通過CAS ticket緩存取到了token,并將ticket和token的緩存數據都刪除了。
修改部分
// 設置超時時間redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, token);redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME*2 / 1000);redisUtil.set(CommonConstant.PRIFIX_CAS_TICKET + ticket, token);redisUtil.expire(CommonConstant.PRIFIX_CAS_TICKET + ticket, JwtUtil.EXPIRE_TIME*2 / 1000);總結
以上是生活随笔為你收集整理的CAS单点登出,调整CAS源码,实现前后端分离单点登出、清除redis、shiro登录状态的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: android+直播点赞,Android
- 下一篇: oracle sql execute e