cas是开源的登录认证方案,可以实现多个web应用的单点登录。
随着用户量的增加,web应用需要部署多个实例,要实现不同应用、多实例的共享session,需要先了解cas的logout机制。简单的说,web应用在接入cas的时候需要继承cas-client-core,这个模块完成的事情如下:
@Override public void addSessionById(String mappingId, HttpSession session) { String STKey = getKey(mappingId); StandardSessionFacade standardSessionFacade = (StandardSessionFacade) session; RedisSession redisSession = null; try { redisSession = (RedisSession) getValue(standardSessionFacade, "session"); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (NoSuchFieldException e) { e.printStackTrace(); } if (null == redisSession) { log.error("get redisSession fail"); return; } sessionRedisTemplate.opsForValue().set(STKey, jdkSerializer.serialize(redisSession)); String sessionKey = getKey(session.getId()); stringRedisTemplate.opsForValue().set(sessionKey, STKey); log.debug("cas-client add session, mappingId:" + mappingId + " sessionId:" + session.getId()); }保存ok了,接下来看看删除,先来看看SingleSignOutHandler的删除代码:
public void destroySession(final HttpServletRequest request) { final String logoutMessage = CommonUtils.safeGetParameter(request, this.logoutParameterName); if (log.isTraceEnabled()) { log.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) { String sessionID = session.getId(); if (log.isDebugEnabled()) { log.debug("Invalidating session [" + sessionID + "] for token [" + token + "]"); } try { session.invalidate(); } catch (final IllegalStateException e) { log.debug("Error invalidating session.", e); } } } }invalidate方法会调用StandardSession的expire方法,此处需要利用manager获取上下文信息,但是manager无法存储(测试过使用json序列化或者java 序列化均无法存储manager),因此 invalidate不会真正去执行,看了下代码,只是通知一些监听者。so,感觉问题不大,ST和session对应关系信息删除了,接下来就是删除tomcat的session,干脆用了比较trick的方法,在cas-client-core直接删除tomcat的session。附上删除的代码:
@Override public HttpSession removeSessionByMappingId(String mappingId) { String STKey = getKey(mappingId); log.debug("cas-client remove session, STKey:" + STKey); RedisSession session = null; byte[] value = (byte[]) sessionRedisTemplate.opsForValue().get(STKey); session = (RedisSession) jdkSerializer.deserialize(value); if (null == session) { log.error("session is null"); return null; } removeBySessionById(session.getId()); sessionRedisTemplate.delete(session.getId()); log.debug("delete session:" + session.getId()); return session; } @Override public void removeBySessionById(String sessionId) { log.debug("Attempting to remove Session=[" + sessionId + "]"); String sessionKey = getKey(sessionId); String st = stringRedisTemplate.opsForValue().get(sessionKey); if (log.isDebugEnabled()) { if (st != null) { log.debug("Found mapping for session. Session Removed."); } else { log.debug("No mapping for session found. Ignoring."); } } stringRedisTemplate.delete(sessionKey); sessionRedisTemplate.delete(st); }实现有点trick,测试暂时没有发现问题,两个实例能够正常登陆、使用和退出。