There is no session with id session多人使用一个账号

1.问题场景:在dev和test环境开发时候,分配的账号是多人共用的,当一个人修改权限后,调用shiro的清楚服务器sesionId后,当其他人再次修改权限信息时候,由于服务器的sessionId已经被全部清空,就会报 There is no session with id "XXX"的问题

2.解决方式:网上说的一般是由于SESSIONID和比如tomcat/jetty等使用的sessionId同名导致的,这个是一个原因。不过我的原因是由于服务器所有的sessionId被清空了导致的,所以做了限制:当一个用户登录时候,我会先清空这个账号的所有缓存信息,这样不会导致一个账号在多个地方登录。不过有些简单

package com.sq.transportmanage.gateway.service.common.shiro.realm;

import com.google.common.collect.Maps;
import com.sq.transportmanage.gateway.dao.entity.driverspark.CarAdmUser;
import com.sq.transportmanage.gateway.dao.entity.driverspark.SaasPermission;
import com.sq.transportmanage.gateway.dao.mapper.driverspark.ex.SaasPermissionExMapper;
import com.sq.transportmanage.gateway.dao.mapper.driverspark.ex.SaasRoleExMapper;
import com.sq.transportmanage.gateway.service.auth.MyDataSourceService;
import com.sq.transportmanage.gateway.service.common.constants.Constants;
import com.sq.transportmanage.gateway.service.common.constants.SaasConst;
import com.sq.transportmanage.gateway.service.common.dto.SaasPermissionDTO;
import com.sq.transportmanage.gateway.service.common.shiro.session.RedisSessionDAO;
import com.sq.transportmanage.gateway.service.util.BeanUtil;
import com.sq.transportmanage.gateway.service.util.MD5Utils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.support.DefaultSubjectContext;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.security.NoSuchAlgorithmException;
import java.util.*;

/**认证  与  权限  **/

/**
 * 这个就是shiro SSOLogin 的用户获取的属性配置
 */
@Component
public class UsernamePasswordRealm extends AuthorizingRealm {
    private static final Logger logger = LoggerFactory.getLogger(UsernamePasswordRealm.class);

    @Autowired
    private MyDataSourceService myDataSourceService;

    @Autowired
    private SaasPermissionExMapper saasPermissionExMapper;

    @Autowired
    private SaasRoleExMapper saasRoleExMapper;

    @Autowired
    @Qualifier("sessionDAO")
    private RedisSessionDAO redisSessionDAO;

    /**重写:获取用户的身份认证信息**/
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException{
        logger.info( "[获取用户的身份认证信息开始]authenticationToken="+authenticationToken);
        try {
            UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;
            CarAdmUser adMUser = myDataSourceService.queryByAccount(token.getUsername());

            //处理session 防止一个账号多处登录
            try {
                redisSessionDAO.clearRelativeSession(null,null,adMUser.getUserId());
            } catch (Exception e) {
                logger.info("=========清除session异常============");
            }

            SSOLoginUser loginUser = new SSOLoginUser();
            loginUser.setId( adMUser.getUserId() );
            loginUser.setLoginName( adMUser.getAccount() );
            loginUser.setMobile( adMUser.getPhone() );
            loginUser.setName( adMUser.getUserName() );
            loginUser.setEmail(adMUser.getEmail());
            loginUser.setType(null);   //
            loginUser.setStatus( adMUser.getStatus() );
            loginUser.setAccountType( adMUser.getAccountType() );
            loginUser.setLevel(adMUser.getLevel());
            loginUser.setMerchantId(adMUser.getMerchantId());
            loginUser.setSupplierIds(adMUser.getSuppliers());
            String md5= null;
            try {
                md5 = MD5Utils.getMD5DigestBase64(loginUser.getMerchantId().toString());
            } catch (NoSuchAlgorithmException e) {
                logger.info("sign error" + e);
            }
            if(Constants.MANAGE_MD5.equals(md5)){
                loginUser.setSuper(true);
            }else {
                loginUser.setSuper(false);
            }
            List<String> menuUrlList = saasPermissionExMapper.queryPermissionMenussOfUser(adMUser.getUserId());
            loginUser.setMenuUrlList(menuUrlList);
            /**当前用户所拥有的菜单权限**/
            List<Integer> permissionIds = saasPermissionExMapper.queryPermissionIdsOfUser(adMUser.getUserId());

            List<Byte> permissionTypes =  Arrays.asList( new Byte[] { SaasConst.PermissionType.MENU });

            Map<Integer,List<SaasPermissionDTO>> mapPermission = Maps.newHashMap();

            /**查询所有的一级菜单**/
            if(!CollectionUtils.isEmpty(permissionIds)){
                List<SaasPermission> permissionList = saasPermissionExMapper.queryModularPermissions(permissionIds);
                Map<Integer,String> map = Maps.newHashMap();
                permissionList.forEach(list ->{
                    map.put(list.getPermissionId(),list.getPermissionName());
                    //查询所有一级菜单下的子菜单 以树形结果返回
                    List<SaasPermissionDTO> menuPerms = this.getChildren( permissionIds , list.getPermissionId(), permissionTypes);
                    mapPermission.put(list.getPermissionId(),menuPerms);
                });

                loginUser.setMenuPermissionMap(map);
                loginUser.setMapPermission(mapPermission);
            }
            //

            //---------------------------------------------------------------------------------------------------------数据权限BEGIN

            logger.info( "[获取用户的身份认证信息]="+loginUser);
            return new SimpleAuthenticationInfo(loginUser, authenticationToken.getCredentials()  ,  this.getName() );
        } catch (Exception e) {
            logger.error("获取用户的身份认证信息异常",e);
            return null;
        }
    }

    /**
     * 查询每个一级菜单下的子菜单
     * @param permissionIds
     * @param parentPermissionId
     * @param permissionTypes  tree 树形,list 列表
     * @return
     */
    private List<SaasPermissionDTO> getChildren( List<Integer> permissionIds,  Integer parentPermissionId,  List<Byte> permissionTypes ){
        List<SaasPermission> childrenPos = saasPermissionExMapper.queryPermissions(permissionIds, parentPermissionId, null, permissionTypes, null, null);
        if(childrenPos==null || childrenPos.size()==0) {
            return null;
        }
        //递归
        List<SaasPermissionDTO> childrenDtos = BeanUtil.copyList(childrenPos, SaasPermissionDTO.class);
        Iterator<SaasPermissionDTO> iterator = childrenDtos.iterator();
        while (iterator.hasNext()) {
            SaasPermissionDTO childrenDto = iterator.next();
            List<SaasPermissionDTO> childs = this.getChildren( permissionIds, childrenDto.getPermissionId() ,  permissionTypes );
            childrenDto.setChildPermissions(childs);
        }
        return childrenDtos;
    }

    /**
     * 查询角色登录进来所拥有的菜单时候shiro实现
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        SSOLoginUser loginUser = (SSOLoginUser) principalCollection.getPrimaryPrincipal();
        String account = loginUser.getLoginName(); //登录名

        List<String> perms_string = saasPermissionExMapper.queryPermissionCodesOfUser(  loginUser.getId() );
        List<String> roles_string   = saasRoleExMapper.queryRoleCodesOfUser( loginUser.getId() );

        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        Set<String> roles = new HashSet<String>( roles_string );
        authorizationInfo.setRoles( roles );
        logger.info( "[获取用户授权信息(角色)] "+account+"="+roles);

        Set<String> perms = new HashSet<String>( perms_string );
        authorizationInfo.setStringPermissions(perms);
        logger.info( "[获取用户授权信息(权限)] "+account+"="+perms);
        return authorizationInfo;
    }

    @Override
    public Object getAuthorizationCacheKey(PrincipalCollection principals) {
        SSOLoginUser loginUser = (SSOLoginUser) principals.getPrimaryPrincipal();
        String account = loginUser.getLoginName(); //登录名
        return "-AuthInfo-"+account;
    }

    @Override
    public void clearCachedAuthorizationInfo(PrincipalCollection principals) {
        super.clearCachedAuthorizationInfo(principals);
    }
    @Override
    public void clearCachedAuthenticationInfo(PrincipalCollection principals) {
        super.clearCachedAuthenticationInfo(principals);
    }
    @Override
    public void clearCache(PrincipalCollection principals) {
        super.clearCache(principals);
    }
}
/**二、当权限信息、角色信息、用户信息发生变化时,同时清理与之相关联的会话**/
    @MyDataSource(value = DataSourceType.DRIVERSPARK_MASTER)
    public void clearRelativeSession( final Integer permissionId, final  Integer roleId, final  Integer userId ) {
         final Cache<Serializable, Session> cache = super.getActiveSessionsCache();
        //final Cache<Serializable, Session> cache = activeSessions;
        new Thread(new Runnable() {
            @SuppressWarnings("unchecked")
            @Override
            public void run() {
                try{
                     //A:如果当权限发生变化时,查询所关联的全部角色ID
                    List<Integer> roleIds = new ArrayList<Integer>();
                    if( permissionId!=null ) {
                        roleIds = myDataSourceService.queryRoleIdsOfPermission( permissionId );
                    }
                    //B:如果当角色发生变化时,查询所关联的用户ID
                    if( roleId !=null ) {
                        roleIds.add(roleId);
                    }
                    List<Integer> userIds = new ArrayList<Integer>();
                    if( roleIds.size()>0 ) {
                        userIds = myDataSourceService.queryUserIdsOfRole( roleIds );
                    }
                    //C:如果当用户发生变化时,查询出这些用户的登录账户名称
                    if( userId != null ) {
                        logger.info("当用户发生变化时清除缓存userId={}",userId);
                        userIds.add(userId);
                    }
                    List<String> accounts = new ArrayList<String>();
                    if(userIds.size()>0) {
                        accounts = myDataSourceService.queryAccountsOfUsers(userIds);
                    }
                    //D:汇总需要清理的REDIS KEY 和 sessionId
                    if(accounts.size() ==0) {
                        return;
                    }
                    Set<String> redisKeysNeedDelete = new HashSet<String>();//这是需要清除的所有REDIS KEY
                    Set<String> allSessionIds              = new HashSet<String>();//这是需要清除的所有的sessionId
                    for( String account : accounts) {
                        redisKeysNeedDelete.add( KEY_PREFIX_OF_SESSIONID + account );
                        Set<String> sessionIds  =  (Set<String>) redisTemplate.opsForValue().get(KEY_PREFIX_OF_SESSIONID+account);
                        if(sessionIds!=null && sessionIds.size()>0) {
                            allSessionIds.addAll(sessionIds);
                        }
                    }

                    //E1:执行清除执久化的会话(这里是保存在REDIS中的)
                    for( String sessionId : allSessionIds) {
                        logger.info("执行清除REDIS的会话缓存sessionId={}",sessionId);
                        redisKeysNeedDelete.add( KEY_PREFIX_OF_SESSION + sessionId );
                    }
                    redisTemplate.delete(redisKeysNeedDelete);
                    //E2:执行清理shiro会话缓存
                    if(cache!=null) {
                        for(String sessionId : allSessionIds ){
                            SimpleSession session = (SimpleSession)cache.get(sessionId);
                            if(session!=null) {
                                session.setExpired(true);
                            }
                            logger.info("执行清理shiro会话缓存sessionId={}",sessionId);
                            cache.remove(sessionId);
                        }
                    }
                    //E3:执行清理shiro 认证与授权缓存
                    for( String account : accounts) {
                        logger.info("执行清理shiro 认证与授权缓存account={}",account);
                        //todo 此处不合理,应该用下面的代码 这个是临时方案:执行退出的操作 相当于手动点击退出 这个触发条件太广泛了 要加很多逻辑判断那些人需要退出
                        /*Subject subject = SecurityUtils.getSubject();
                        if(subject.isAuthenticated()) {
                            subject.logout();
                        }*/

                        SSOLoginUser principal  = new SSOLoginUser();
                        principal.setLoginName(  account );

                        SimplePrincipalCollection simplePrincipalCollection = new SimplePrincipalCollection( );
                        simplePrincipalCollection.add(principal, authorizingRealm.getName() );
                        ((UsernamePasswordRealm)authorizingRealm).clearCache( simplePrincipalCollection );
                    }
                }catch(Exception ex) {
                    logger.error("清除缓存异常",ex);
                }finally {
                    //DynamicRoutingDataSource.setDefault("mdbcarmanage-DataSource");
                }
            }
        }).start();
    }

原文地址:https://www.cnblogs.com/thinkingandworkinghard/p/12530007.html

时间: 2024-10-09 00:46:52

There is no session with id session多人使用一个账号的相关文章

解决org.apache.shiro.session.UnknownSessionException: There is no session with id的问题

一.背景 最近在整合了Spring+Shiro+Redis实现tomcat集群session共享的问题之后,发布以后运行以后发现老是会出现:org.apache.shiro.session.UnknownSessionException: There is no session with id [xxxx]的问题,具体问题如下截图: 二.出现这个问题的原因 只所以出现这个问题是因为在shiro的DefaultWebSessionManager类中,默认Cookie名称是JSESSIONID,这样

Tomcat的基础知识和一些应用,session cluster和session server

Tomcat简单来说就是JAVA 2 EE 加上Servlet和JSP类库的实现,tomcat=JWS(Sun)+ Jserv(ASF) Tomcat的核心组件: catalina:servlet container Coyote:http connection Jasper:JSP Engine 执行引擎 TomcatInstance: 运行中的tomcat进程(java进程) Server:即一个tomcat实例: Service:用于将connector关联至engine组件:一个serv

cookie和session的区别,session的生命周期,

这些都是基础知识,不过有必要做深入了解.先简单介绍一下. 二者的定义: 当你在浏览网站的时候,WEB 服务器会先送一小小资料放在你的计算机上,Cookie 会帮你在网站上所打的文字或是一些选择, 都纪录下来.当下次你再光临同一个网站,WEB 服务器会先看看有没有它上次留下的 Cookie 资料,有的话,就会依据 Cookie 里的内容来判断使用者,送出特定的网页内容给你. Cookie 的使用很普遍,许多有提供个人化服务的网站,都是利用 Cookie 来辨认使用者,以方便送出使用者量身定做的内容

session.load()和session.get()的区别

Session.load/get方 法均可以根据指定的实体类和id从数据库读取记录,并返回与之对应的实体对象. 其区别在于: 如果未能发现 符合条件的记录,get方法返回null, 而load方 法会抛出一个ObjectNotFoundException. Hibernate的工作原理: 一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装, 使得java程序员可以使用对象编程思维来操纵数据库. 工作流程: 1.读 取并解析配置文件 2.读取并解析映射信息,创建Session

高性能Web服务之Httpd负载均衡Tomcat实现Session Sticky及Session Cluster

Httpd负载均衡Tomcat实现Session Sticky及Session Cluster架构如下所示: 实现过程如下: 配置tomcat服务(tomcat1\tomcat2) (1)安装JDK # rpm -ivh jdk-7u9-linux-x64.rpm  --安装JDK后生成的文件 # cd /usr/java/ ; ll total 4 lrwxrwxrwx  1 root root   16 Sep 27 09:09 default -> /usr/java/latest drw

[session]PHP的session机制,配置与高级应用

--------------------------------------------------------------------------------------------------------- 一. PHP的session原理: 1.开启session_start(),服务器端产生session文件,将相关信息存储到这个文件中,文件以"sess_"为前缀,连接session_id()值来命名(内容是由特定算法生成的用户的明文信息,大型项目则须进行加密处理),同时将se

报表session与应用session常识普及

1. 报表session与应用session 报表集成到项目中可能会有一个疑问就是系统应用和报表应用在一个web服务器下,那系统session和报表session是不是一个session呢?如果不是那会不会有冲突呢? 答案是:不是同一个session也不会起冲突,因为应用的session存放的是request请求等一些共享信息,而报表session存放的是访问报表的相关信息,如访问的是不是同一个模板等,两者是完全独立,所以不会冲突. 2. 报表开发工具FineReport中创建与关闭sessio

深度实现session【包括session入库、session机制和session和cookie的使用方法,完善会话机制(在分布式机器中也能使用)】、无限分类的实现

1.session的注意点:@session_start();//这个配置需要注意,session开启中会有影响,所以使用错误抑制符进行限制[并且使用php.ini对session进行自动开启] session_start()前的输出问题:[session信息本身会增加到http头信息,也就是http主体不能在头前]对php.ini中的输出缓存进行配置,out_buffer的配置[注意:开启之后能够保证输出内容在脚本中缓存] [注意](1)脚本中session变量的键只能是字符串类型的[$_SE

如何使用Spring Session实现分布式Session管理

Spring Session作为Spring社区官方推荐的一个比较简单快速的Java Web分布式session解决方案,帮我们搞定了长期以来比较蛋疼的session分布式的问题. Spring Session解决的基本思路很简单,即将用户的session信息全部存放到一个redis数据库中,所有的session都从这个数据库拿.由于redis是一个内存数据库,数据信息读写是非常快速的.如此一来,多个Tomcat,共用一个redis数据库,即实现了session的共享问题. 鉴于目前很少看到Sp