public class SessionRegistryImpl implements SessionRegistry, ApplicationListener<SessionDestroyedEvent> { protected final Log logger = LogFactory.getLog(SessionRegistryImpl.class); private final ConcurrentMap<Object, Set<String>> principals; private final Map<String, SessionInformation> sessionIds;
public SessionRegistryImpl() { this.principals = new ConcurrentHashMap(); this.sessionIds = new ConcurrentHashMap(); }
public List<Object> getAllPrincipals() { return new ArrayList(this.principals.keySet()); }
public List<SessionInformation> getAllSessions(Object principal, boolean includeExpiredSessions) { //代码省略 }
public SessionInformation getSessionInformation(String sessionId) { //代码省略 }
public void registerNewSession(String sessionId, Object principal) { Assert.hasText(sessionId, "SessionId required as per interface contract"); Assert.notNull(principal, "Principal required as per interface contract"); if (this.getSessionInformation(sessionId) != null) { this.removeSessionInformation(sessionId); }
if (this.logger.isDebugEnabled()) { this.logger.debug("Registering session " + sessionId + ", for principal " + principal); }
this.sessionIds.put(sessionId, new SessionInformation(principal, sessionId, new Date())); this.principals.compute(principal, (key, sessionsUsedByPrincipal) -> { if (sessionsUsedByPrincipal == null) { sessionsUsedByPrincipal = new CopyOnWriteArraySet(); }
((Set)sessionsUsedByPrincipal).add(sessionId); if (this.logger.isTraceEnabled()) { this.logger.trace("Sessions used by '" + principal + "' : " + sessionsUsedByPrincipal); }
return (Set)sessionsUsedByPrincipal; }); }
public void removeSessionInformation(String sessionId) { Assert.hasText(sessionId, "SessionId required as per interface contract"); SessionInformation info = this.getSessionInformation(sessionId); if (info != null) { if (this.logger.isTraceEnabled()) { this.logger.debug("Removing session " + sessionId + " from set of registered sessions"); }
this.sessionIds.remove(sessionId); this.principals.computeIfPresent(info.getPrincipal(), (key, sessionsUsedByPrincipal) -> { if (this.logger.isDebugEnabled()) { this.logger.debug("Removing session " + sessionId + " from principal's set of registered sessions"); }
sessionsUsedByPrincipal.remove(sessionId); if (sessionsUsedByPrincipal.isEmpty()) { if (this.logger.isDebugEnabled()) { this.logger.debug("Removing principal " + info.getPrincipal() + " from registry"); }
sessionsUsedByPrincipal = null; }
if (this.logger.isTraceEnabled()) { this.logger.trace("Sessions used by '" + info.getPrincipal() + "' : " + sessionsUsedByPrincipal); }
this.principals.compute(principal, (key, sessionsUsedByPrincipal) -> { if (sessionsUsedByPrincipal == null) { sessionsUsedByPrincipal = new CopyOnWriteArraySet(); }
((Set)sessionsUsedByPrincipal).add(sessionId); if (this.logger.isTraceEnabled()) { this.logger.trace("Sessions used by '" + principal + "' : " + sessionsUsedByPrincipal); }
this.sessionIds.remove(sessionId); this.principals.computeIfPresent(info.getPrincipal(), (key, sessionsUsedByPrincipal) -> { if (this.logger.isDebugEnabled()) { this.logger.debug("Removing session " + sessionId + " from principal's set of registered sessions"); }
sessionsUsedByPrincipal.remove(sessionId); if (sessionsUsedByPrincipal.isEmpty()) { if (this.logger.isDebugEnabled()) { this.logger.debug("Removing principal " + info.getPrincipal() + " from registry"); }
sessionsUsedByPrincipal = null; }
if (this.logger.isTraceEnabled()) { this.logger.trace("Sessions used by '" + info.getPrincipal() + "' : " + sessionsUsedByPrincipal); } return sessionsUsedByPrincipal; });
@Override public boolean equals(Object rhs) { return rhs instanceof User ? this.username.equals(((User)rhs).username) : false; }
@Override public int hashCode() { return this.username.hashCode(); }
当然用户也可以根据自己的逻辑来进行实现,如下所示的实现逻辑:
1 2 3 4 5 6 7 8 9 10 11 12
@Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; User user = (User) o; return Objects.equals(username, user.username); }
@Override public int hashCode() { return Objects.hash(username); }
在完成上述配置以后,重启项目,再次测试多端登录,你就会发现可以踢掉已经登录的用户。当然了本篇不仅仅只限于使用Spring Data JPA的情况,当开发者使用了Mybatis等其他ORM框架时,也只需和本文一样,重写ConcurrentMap集合principals对象的Key类型的equals和hashCode方法。