zoukankan      html  css  js  c++  java
  • SpringBoot,Security4, redis共享session,分布式SESSION并发控制,同账号只能登录一次

    由于集成了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

  • 相关阅读:
    对数线性模型与线性链条件随机场
    25匹马,5个跑道,每个跑道最多能有1匹马进行比赛,最少比多少次能比出前3名?前5名?
    SVM 与 LR的异同
    EM算法简易推导
    K-means算法的优缺点
    自助采样包含训练集里63.2%的样本?
    指数加权移动平均
    oracle 对于用户的相关操作
    docker 安装 maven 私有库 nexus3
    idea 自动注入@Autowired 警告 Field injection is not recommended 关闭
  • 原文地址:https://www.cnblogs.com/javalinux/p/14632976.html
Copyright © 2011-2022 走看看