博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
spring security 在没实现session共享的集群环境下 防止用户多次登录的 实现思路
阅读量:2022 次
发布时间:2019-04-28

本文共 9422 字,大约阅读时间需要 31 分钟。

背景

  • 项目采用阿里云负载均衡,基于cookie的会话保持。
  • 没有实现集群间的session共享。
  • 项目采用spring security 并且配置了session策略如下:

一个账户只对应一个session,也就是一个用户在不同浏览器登陆,后登陆的会导致前面登陆的session失效。

问题分析

集群环境下,导致maximumSessions的配置失效。并不能实现预期的目标。因为session没有共享。

解决思路

  • 采用spring data redis session解决实现session共享,统一管理。

    但是由于项目集成了过多的开源框架,由于版本原因,很难整合到一起。并且项目测试已经接近尾声,因此没有采用。

  • zookeeper监听用户session 方式,后登陆时操作对应节点,触发监听事件,使其先创建的session失效。

最终采用zookeeper监听session方式

具体代码

session上下文保持

package com.raymon.cloudq.util;import javax.servlet.http.HttpSession;import java.util.Map;import java.util.concurrent.ConcurrentHashMap;public class SessionContext {
private static SessionContext instance; private Map
sessionMap; private SessionContext() { sessionMap = new ConcurrentHashMap
(); } public synchronized static SessionContext getInstance() { if (instance == null) { instance = new SessionContext(); } return instance; } public void addSession(HttpSession session) { if (session != null) { sessionMap.put(session.getId(), session); } } public void delSession(HttpSession session) { if (session != null) { sessionMap.remove(session.getId()); } } public void delSession(String sessionId) { sessionMap.remove(sessionId); } public HttpSession getSession(String sessionId) { if (sessionId == null) return null; return sessionMap.get(sessionId); }}

基于zookeeper的session控制接口

** * 由于session在集群中没有实现共享,一个账户只能对应一个session * 基于zookeeper监听的 来控制 */public interface ClusterSessionsCtrlService {
/** * 设置监听 */ void setMaximumSessionsListener(); /** * 注册zookeeper sesssion数据 * 后登陆的用户会删除先登录的节点,触发监听,让先登陆的session失效 * @param empId * @param httpSession */ void registerZookeeperSession(Integer empId,HttpSession httpSession); /** * session超时,删除zookeeper注册数据 * @param sessionId */ void deleteZookeeperSessionRegister(String sessionId);}

session控制接口实现类

package com.raymon.cloudq.service.impl;import com.raymon.cloudq.service.ClusterSessionsCtrlService;import com.raymon.cloudq.util.SessionContext;import org.apache.commons.lang.StringUtils;import org.apache.curator.RetryPolicy;import org.apache.curator.framework.CuratorFramework;import org.apache.curator.framework.CuratorFrameworkFactory;import org.apache.curator.framework.api.transaction.CuratorOp;import org.apache.curator.framework.api.transaction.CuratorTransactionResult;import org.apache.curator.framework.recipes.cache.PathChildrenCache;import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;import org.apache.curator.retry.ExponentialBackoffRetry;import org.apache.zookeeper.CreateMode;import org.apache.zookeeper.data.Stat;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.InitializingBean;import org.springframework.beans.factory.annotation.Value;import org.springframework.stereotype.Service;import javax.servlet.http.HttpSession;import java.util.ArrayList;import java.util.Collection;import java.util.List;@Servicepublic class ClusterSessionsCtrlServiceImpl implements ClusterSessionsCtrlService, InitializingBean {    @Value(" ${zookeeperhostName}")    private String zookeeperConnectionString;    private static String sessionPath = "/session";    private static String sessionReaPath = "/sessionrea";    protected static org.slf4j.Logger logger = LoggerFactory.getLogger(ClusterSessionsCtrlServiceImpl.class);    private CuratorFramework client = null;    @Override    public void afterPropertiesSet() throws Exception {        setMaximumSessionsListener();    }    @Override    public void setMaximumSessionsListener() {        RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);        client = CuratorFrameworkFactory.builder().connectString(zookeeperConnectionString)                .sessionTimeoutMs(8000).retryPolicy(retryPolicy).build();        client.start();        try {            Stat stat = client.checkExists().forPath(sessionPath);            if (stat == null) {                client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).forPath(sessionPath);            }            stat = client.checkExists().forPath(sessionReaPath);            if (stat == null) {                client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).forPath(sessionReaPath);            }        } catch (Exception e) {            e.printStackTrace();            logger.error("zookeeper创建/session失败,原因{}", e.toString());        }        PathChildrenCache cache = null;        try {            cache = new PathChildrenCache(client, sessionPath, true);            cache.start();        } catch (Exception e) {            e.printStackTrace();            logger.error(e.toString());        }        PathChildrenCacheListener cacheListener = new PathChildrenCacheListener() {            @Override            public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {                logger.info("事件类型:" + event.getType());                if( event.getData()!=null){                    logger.info("节点数据:" + event.getData().getPath() + " = " + new String(event.getData().getData()));                }                if (event.getType().equals(PathChildrenCacheEvent.Type.CHILD_REMOVED)) {                    HttpSession httpSession = SessionContext.getInstance().getSession(new String(event.getData().getData()));                    if (httpSession == null) {                        return;                    }                    httpSession.invalidate();                }            }        };        cache.getListenable().addListener(cacheListener);    }    @Override    public void deleteZookeeperSessionRegister(String sessionId) {        try {            SessionContext.getInstance().delSession(sessionId);            String empId = null;            Stat stat = client.checkExists().forPath(sessionReaPath + "/" + sessionId);            if (stat != null) {                empId = new String(client.getData().forPath(sessionReaPath + "/" + sessionId));                client.delete().forPath(sessionReaPath + "/" + sessionId);                logger.info("delete session:" + sessionReaPath + "/" + sessionId);            }         /*   stat = client.checkExists().forPath(sessionPath + "/" + empId);            if (StringUtils.isNotEmpty(empId) && stat != null) {                client.delete().forPath(sessionPath + "/" + empId);                logger.info("delete session:" + sessionPath + "/" + empId);            }*/        } catch (Exception e) {            e.printStackTrace();            logger.error(e.toString());        }    }    @Override    public void registerZookeeperSession(Integer empId, HttpSession httpSession) {        try {            SessionContext.getInstance().addSession(httpSession);            Stat stat = client.checkExists().forPath(sessionPath + "/" + empId);            List
operations = new ArrayList
(); if (stat != null) { CuratorOp deleteOpt = client.transactionOp().delete().forPath(sessionPath + "/" + empId); operations.add(deleteOpt); } CuratorOp createSessionPathOpt = client.transactionOp().create().withMode(CreateMode.EPHEMERAL).forPath(sessionPath + "/" + empId, httpSession.getId().getBytes()); CuratorOp createSessionReaPathOpt = client.transactionOp().create().withMode(CreateMode.EPHEMERAL).forPath(sessionReaPath + "/" + httpSession.getId(), String.valueOf(empId).getBytes()); operations.add(createSessionPathOpt); operations.add(createSessionReaPathOpt); Collection
results = client.transaction().forOperations(operations); for (CuratorTransactionResult result : results) { logger.info(result.getForPath() + " - " + result.getType()); } } catch (Exception e) { e.printStackTrace(); logger.error(e.toString()); } }}

session监听

/** * session监听 *  * */public class SessionListener  implements HttpSessionListener,  HttpSessionAttributeListener{
private SessionContext context = SessionContext.getInstance(); Logger log = LoggerFactory.getLogger(SessionListener.class); @Override public void attributeAdded(HttpSessionBindingEvent arg0) { } @Override public void attributeRemoved(HttpSessionBindingEvent arg0) { } @Override public void attributeReplaced(HttpSessionBindingEvent arg0) { } @Override public void sessionCreated(HttpSessionEvent arg0) { if(log.isDebugEnabled()) { log.debug("创建session"); } } @Override public void sessionDestroyed(HttpSessionEvent arg0) { context.delSession(arg0.getSession()); ApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(arg0.getSession().getServletContext()); ClusterSessionsCtrlService clusterSessionsCtrlService = ctx.getBean(ClusterSessionsCtrlService.class); clusterSessionsCtrlService.deleteZookeeperSessionRegister(arg0.getSession().getId()); }}

用户登陆成功需要注册session监听

clusterSessionsCtrlService.registerZookeeperSession(empId,request.getSession());

转载地址:http://xdgxf.baihongyu.com/

你可能感兴趣的文章
前端要给力之:分解对象构造过程new()
查看>>
Qomo OpenProject Field Test 3发布!
查看>>
数据库视频总结
查看>>
音标学习--元音
查看>>
微习惯--简单到四个
查看>>
罗塞塔一遍总结
查看>>
如何将音频放慢
查看>>
暑假总结
查看>>
SQL Server解决18456错误方案
查看>>
SQL数据库连接问题
查看>>
英语月总结
查看>>
sql server 附加数据库 错误5123 解决办法
查看>>
音标游戏阶段总结
查看>>
随写 171015
查看>>
自考随笔
查看>>
赌约而至
查看>>
机房登录91错误
查看>>
授之以愉(上)
查看>>
招聘会感悟
查看>>
洽谈你的情商
查看>>