SpringBoot,Security4, redis共享session,分布式SESSION并发控制,同账号只能登录一次 - 天涯爪哇岛的个人空间 - OSCHINA

SpringBoot,Security4, redis共享session,分布式SESSION并发控制,同账号只能登录一次


[

天涯爪哇岛

 天涯爪哇岛](https://my.oschina.net/u/1999167) 发布于 2018/04/05 16:51

字数 982

阅读 1600

收藏 0

点赞 0

评论 0

为什么80%的码农都做不了架构师?>>>

原文:https://www.cnblogs.com/sweetchildomine/p/7007242.html

由于集成了spring session ,redis 共享session,导致SpringSecurity单节点的session并发控制失效,

springSession 号称 无缝整合httpsession,这个应该是没问题的,

但是为什么分布式情况下的session 并发依然是单节点呢?

因为session并发控制是第三方框架的 单节点缓存了session名单.我们要重写框架这一部分代码,把session名单存入到redis.

关于SpringSecruity的Session并发管理,看我另一篇随笔:

SpringBoot整合SpringSecurity,SESSION 并发管理,同账号只允许登录一次

废话说到,这里,看代码:

重写 SessionRegistry

复制代码

**
* Created by 为 on 2017-6-9.
*/

public class MySessionRegistryImpl implements SessionRegistry, ApplicationListener<SessionDestroyedEvent> {

private static final String SESSIONIDS = "sessionIds";

private static final String PRINCIPALS = "principals";

@Resource
private RedisTemplate redisTemplate;

protected final Log logger = LogFactory.getLog(SessionRegistryImpl.class);

// private final ConcurrentMap<Object, Set<String>> principals = new ConcurrentHashMap();
// private final Map<String, SessionInformation> sessionIds = new ConcurrentHashMap();

public MySessionRegistryImpl() {
}

public List<Object> getAllPrincipals() {
    return new ArrayList(this.getPrincipalsKeySet());
}

public List<SessionInformation> getAllSessions(Object principal, boolean includeExpiredSessions) {
    Set<String> sessionsUsedByPrincipal =  this.getPrincipals(((UserDetails)principal).getUsername());
    if (sessionsUsedByPrincipal == null) {
        return Collections.emptyList();
    } else {
        List<SessionInformation> list = new ArrayList(sessionsUsedByPrincipal.size());
        Iterator var5 = sessionsUsedByPrincipal.iterator();

        while (true) {
            SessionInformation sessionInformation;
            do {
                do {
                    if (!var5.hasNext()) {
                        return list;
                    }

                    String sessionId = (String) var5.next();
                    sessionInformation = this.getSessionInformation(sessionId);
                } while (sessionInformation == null);
            } while (!includeExpiredSessions && sessionInformation.isExpired());

            list.add(sessionInformation);
        }
    }
}

public SessionInformation getSessionInformation(String sessionId) {
    Assert.hasText(sessionId, "SessionId required as per interface contract");
    return (SessionInformation) this.getSessionInfo(sessionId);
}

public void onApplicationEvent(SessionDestroyedEvent event) {
    String sessionId = event.getId();
    this.removeSessionInformation(sessionId);
}

public void refreshLastRequest(String sessionId) {
    Assert.hasText(sessionId, "SessionId required as per interface contract");
    SessionInformation info = this.getSessionInformation(sessionId);
    if (info != null) {
        info.refreshLastRequest();
    }

}

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.logger.isDebugEnabled()) {
        this.logger.debug("Registering session " + sessionId + ", for principal " + principal);
    }

    if (this.getSessionInformation(sessionId) != null) {
        this.removeSessionInformation(sessionId);
    }

    this.addSessionInfo(sessionId, new SessionInformation(principal, sessionId, new Date()));

// this.sessionIds.put(sessionId, new SessionInformation(principal, sessionId, new Date()));

    Set<String> sessionsUsedByPrincipal = (Set) this.getPrincipals(principal.toString());
    if (sessionsUsedByPrincipal == null) {
        sessionsUsedByPrincipal = new CopyOnWriteArraySet();
        Set<String> prevSessionsUsedByPrincipal = (Set) this.putIfAbsentPrincipals(principal.toString(), sessionsUsedByPrincipal);
        if (prevSessionsUsedByPrincipal != null) {
            sessionsUsedByPrincipal = prevSessionsUsedByPrincipal;
        }
    }

    ((Set) sessionsUsedByPrincipal).add(sessionId);
    this.putPrincipals(principal.toString(), sessionsUsedByPrincipal);
    if (this.logger.isTraceEnabled()) {
        this.logger.trace("Sessions used by '" + principal + "' : " + 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.removeSessionInfo(sessionId);
        Set<String> sessionsUsedByPrincipal = (Set) this.getPrincipals(info.getPrincipal().toString());
        if (sessionsUsedByPrincipal != null) {
            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");
                }

                this.removePrincipal(((UserDetails)info.getPrincipal()).getUsername());
            }

            if (this.logger.isTraceEnabled()) {
                this.logger.trace("Sessions used by '" + info.getPrincipal() + "' : " + sessionsUsedByPrincipal);
            }

        }
    }
}


public void addSessionInfo(final String sessionId, final SessionInformation sessionInformation) {
    BoundHashOperations<String, String, SessionInformation> hashOperations = redisTemplate.boundHashOps(SESSIONIDS);
    hashOperations.put(sessionId, sessionInformation);
}

public SessionInformation getSessionInfo(final String sessionId) {
    BoundHashOperations<String, String, SessionInformation> hashOperations = redisTemplate.boundHashOps(SESSIONIDS);
    return hashOperations.get(sessionId);
}

public void removeSessionInfo(final String sessionId) {
    BoundHashOperations<String, String, SessionInformation> hashOperations = redisTemplate.boundHashOps(SESSIONIDS);
    hashOperations.delete(sessionId);
}

public Set<String> putIfAbsentPrincipals(final String key, final Set<String> set) {
    BoundHashOperations<String, String, Set<String>> hashOperations = redisTemplate.boundHashOps(PRINCIPALS);
    hashOperations.putIfAbsent(key, set);
    return hashOperations.get(key);
}

public void putPrincipals(final String key, final Set<String> set) {
    BoundHashOperations<String, String, Set<String>> hashOperations = redisTemplate.boundHashOps(PRINCIPALS);
    hashOperations.put(key,set);
}

public Set<String> getPrincipals(final String key) {
    BoundHashOperations<String, String, Set<String>> hashOperations = redisTemplate.boundHashOps(PRINCIPALS);
    return hashOperations.get(key);
}

public Set<String> getPrincipalsKeySet() {
    BoundHashOperations<String, String, Set<String>> hashOperations = redisTemplate.boundHashOps(PRINCIPALS);
    return hashOperations.keys();
}

public void removePrincipal(final String key) {
    BoundHashOperations<String, String, Set<String>> hashOperations = redisTemplate.boundHashOps(PRINCIPALS);
    hashOperations.delete(key);
}

}

复制代码

重写ConcurrentSessionControlAuthenticationStrategy

复制代码

/**
* Created by 为 on 2017-6-14.
*/
public class MyConcurrentSessionControlAuthenticationStrategy extends ConcurrentSessionControlAuthenticationStrategy {

protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
private final SessionRegistry sessionRegistry;
private boolean exceptionIfMaximumExceeded = false;
private int maximumSessions = 1;

public MyConcurrentSessionControlAuthenticationStrategy(SessionRegistry sessionRegistry) {
    super(sessionRegistry);
    Assert.notNull(sessionRegistry, "The sessionRegistry cannot be null");
    this.sessionRegistry = sessionRegistry;
}

public void onAuthentication(Authentication authentication, HttpServletRequest request, HttpServletResponse response) {
    List<SessionInformation> sessions = this.sessionRegistry.getAllSessions(authentication.getPrincipal(), false);
    int sessionCount = sessions.size();
    int allowedSessions = this.getMaximumSessionsForThisUser(authentication);
    if(sessionCount >= allowedSessions) {
        if(allowedSessions != -1) {
            if(sessionCount == allowedSessions) {
                HttpSession session = request.getSession(false);
                if(session != null) {
                    Iterator var8 = sessions.iterator();

                    while(var8.hasNext()) {
                        SessionInformation si = (SessionInformation)var8.next();
                        if(si.getSessionId().equals(session.getId())) {
                            return;
                        }
                    }
                }
            }

            this.allowableSessionsExceeded(sessions, allowedSessions, this.sessionRegistry);
        }
    }
}

protected int getMaximumSessionsForThisUser(Authentication authentication) {
    return this.maximumSessions;
}

protected void allowableSessionsExceeded(List<SessionInformation> sessions, int allowableSessions, SessionRegistry registry) throws SessionAuthenticationException {
    if(!this.exceptionIfMaximumExceeded && sessions != null) {
        SessionInformation leastRecentlyUsed = null;
        Iterator var5 = sessions.iterator();

        while(true) {
            SessionInformation session;
            do {
                if(!var5.hasNext()) {
                    leastRecentlyUsed.expireNow();
                    ((MySessionRegistryImpl)sessionRegistry).addSessionInfo(leastRecentlyUsed.getSessionId(),leastRecentlyUsed);
                    return;
                }

                session = (SessionInformation)var5.next();
            } while(leastRecentlyUsed != null && !session.getLastRequest().before(leastRecentlyUsed.getLastRequest()));

            leastRecentlyUsed = session;
        }
    } else {
        throw new SessionAuthenticationException(this.messages.getMessage("ConcurrentSessionControlAuthenticationStrategy.exceededAllowed", new Object\[\]{Integer.valueOf(allowableSessions)}, "Maximum sessions of {0} for this principal exceeded"));
    }
}

public void setExceptionIfMaximumExceeded(boolean exceptionIfMaximumExceeded) {
    this.exceptionIfMaximumExceeded = exceptionIfMaximumExceeded;
}

public void setMaximumSessions(int maximumSessions) {
    Assert.isTrue(maximumSessions != 0, "MaximumLogins must be either -1 to allow unlimited logins, or a positive integer to specify a maximum");
    this.maximumSessions = maximumSessions;
}

public void setMessageSource(MessageSource messageSource) {
    Assert.notNull(messageSource, "messageSource cannot be null");
    this.messages = new MessageSourceAccessor(messageSource);
}

}

复制代码

WebSecurityConfigurerAdapter

复制代码

@Bean

public MyUsernamePasswordAuthenticationFilter myUsernamePasswordAuthenticationFilter() throws Exception {
    MyUsernamePasswordAuthenticationFilter myUsernamePasswordAuthenticationFilter = new MyUsernamePasswordAuthenticationFilter();
    myUsernamePasswordAuthenticationFilter.setPostOnly(true);
    myUsernamePasswordAuthenticationFilter.setAuthenticationManager(this.authenticationManager());
    myUsernamePasswordAuthenticationFilter.setUsernameParameter("name_key");
    myUsernamePasswordAuthenticationFilter.setPasswordParameter("pwd_key");
    myUsernamePasswordAuthenticationFilter.setVerificationCodeParameter("verification_code");
    myUsernamePasswordAuthenticationFilter.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/checkLogin", "POST"));
    myUsernamePasswordAuthenticationFilter.setAuthenticationFailureHandler(simpleUrlAuthenticationFailureHandler());
    myUsernamePasswordAuthenticationFilter.setAuthenticationSuccessHandler(authenticationSuccessHandler());
    myUsernamePasswordAuthenticationFilter.setSessionAuthenticationStrategy(new MyConcurrentSessionControlAuthenticationStrategy(sessionRegistry));
    return myUsernamePasswordAuthenticationFilter;
}

复制代码

开启两个服务,同一个账户登录不同的端口测试,能否被T下线

本文转载自:https://www.cnblogs.com/sweetchildomine/p/7007242.html


Original url: Access
Created at: 2019-06-24 11:57:45
Category: default
Tags: none

请先后发表评论
  • 最新评论
  • 总共0条评论