• shiro会话管理


    一、会话管理

    Shiro独立的会话管理,包含了单点登录的业务场景;Nginx负载多个tomcat;

    1.1 配置会话监听器

    MyShiroSessionListener

    package com.xlb.ssm.shiro;
    
    import org.apache.shiro.session.Session;
    import org.apache.shiro.session.SessionListener;
    
    /**
     * @author 波哥
     * @QQ 2212371722
     * @company 波哥集团
     * @create  2022-08-27 10:55
     */
    public class MyShiroSessionListener implements SessionListener {
    
        //监听会话创建事件
        @Override
        public void onStart(Session session) {
            System.out.println("监听会话创建事件");
            System.out.println("ShiroSessionListener.onstart..."+session.getId());
        }
    
        //监听会话销毁事件
        @Override
        public void onStop(Session session) {
            System.out.println("监听会话销毁事件");
            System.out.println("ShiroSessionListener.onstart..."+session.getId());
        }
    
        //监听会话过期事件
        @Override
        public void onExpiration(Session session) {
            System.out.println("监听会话过期事件");
            System.out.println("ShiroSessionListener.onExpiration..."+session.getId());
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    1.2 配置Shiro.xml

    applicationContext-shiro.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <!--配置自定义的Realm-->
        <bean id="shiroRealm" class="com.xlb.ssm.shiro.MyRealm">
            <property name="userBiz" ref="userBiz" />
            <!--注意:重要的事情说三次~~~~~~此处加密方式要与用户注册时的算法一致 -->
            <!--注意:重要的事情说三次~~~~~~此处加密方式要与用户注册时的算法一致 -->
            <!--注意:重要的事情说三次~~~~~~此处加密方式要与用户注册时的算法一致 -->
            <!--以下三个配置告诉shiro将如何对用户传来的明文密码进行加密-->
            <property name="credentialsMatcher">
                <bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
                    <!--指定hash算法为MD5-->
                    <property name="hashAlgorithmName" value="md5"/>
                    <!--指定散列次数为1024-->
                    <property name="hashIterations" value="1024"/>
                    <!--true指定Hash散列值使用Hex加密存. false表明hash散列值用用Base64-encoded存储-->
                    <property name="storedCredentialsHexEncoded" value="true"/>
                </bean>
            </property>
        </bean>
    
        <!--注册安全管理器-->
        <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
            <property name="realm" ref="shiroRealm" />
        </bean>
    
        <!--Shiro核心过滤器-->
        <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
            <!-- Shiro的核心安全接口,这个属性是必须的 -->
            <property name="securityManager" ref="securityManager" />
            <!-- 身份验证失败,跳转到登录页面 -->
            <property name="loginUrl" value="/login"/>
            <!-- 身份验证成功,跳转到指定页面 -->
            <!--<property name="successUrl" value="/index.jsp"/>-->
            <!-- 权限验证失败,跳转到指定页面 -->
            <property name="unauthorizedUrl" value="/unauthorized.jsp"/>
            <!-- Shiro连接约束配置,即过滤链的定义 -->
            <property name="filterChainDefinitions">
                <value>
                    <!--
                    注:anon,authcBasic,auchc,user是认证过滤器
                        perms,roles,ssl,rest,port是授权过滤器
                    -->
                    <!--anon 表示匿名访问,不需要认证以及授权-->
                    <!--authc表示需要认证 没有进行身份认证是不能进行访问的-->
                    <!--roles[admin]表示角色认证,必须是拥有admin角色的用户才行-->
                    /user/login=anon
                    /user/updatePwd.jsp=authc
                    /admin/*.jsp=roles[4]
                    /user/teacher.jsp=perms[2]
                    
                
            
        
    
        
        
    
        
        
        
    
        
        
            
        
    
        
        
    
        
        
            
            
            
            
            
            
        
    
        
        
            
            
            
            
            
            
            
            
            
            
            
            
            
            
                
                    
                
            
            
            
            
            
        
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120

    测试

    预期结果
    当登录发送请求 会执行 监听器中的 onstart
    当登出发送请求 会执行 监听器中的 onStop
    当检查session过期时,会执行 监听器中的 onExpiration
    过期时,需要身份验证才能访问的方法,就不会被允许访问;

    在这里插入图片描述执行onstart
    在这里插入图片描述访问任意页面
    在这里插入图片描述
    退出
    在这里插入图片描述当会话过期
    在这里插入图片描述

    二、授权使用缓存

    2.1 初始缓存

    利用Map集合存放缓存
    原理:默认从Map(数据库)中查询是否有该缓存,如果没有,则存放入Map集合,在次使用则直接从Map集合中拿

    EhcacheDemo1

    package com.xlb.ssm.shiro;
    
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * 利用map集合简易实现缓存原理
     * @author Administrator
     *
     */
    public class EhcacheDemo1 {
    	static Map<String, Object> cache = new HashMap<String, Object>();
    	//看做查询数据库
    	static Object getValue(String key) {
    		//默认从缓冲中获取数据
    		Object value = cache.get(key);
    		if(value == null) {
    			//从数据库中做查询
    			System.out.println("hello zs");
    			//把查询结果放到缓冲中
    			cache.put(key, new String[] {"zs"});
    			return cache.get(key);
    		}
    		return value;
    	}
    	
    	public static void main(String[] args) {
    		System.out.println(getValue("sname"));
    		System.out.println(getValue("sname"));
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    测试结果
    在这里插入图片描述

    2.2 ehcache.xml介绍

    首先导入pom.xml

    <ehcache.version>2.10.0</ehcache.version>
        <slf4j-api.version>1.7.7</slf4j-api.version>
    
    
    <dependency>
          <groupId>net.sf.ehcache</groupId>
          <artifactId>ehcache</artifactId>
          <version>${ehcache.version}</version>
        </dependency>
    
        <!-- slf4j核心包 -->
        <dependency>
          <groupId>org.slf4j</groupId>
          <artifactId>slf4j-api</artifactId>
          <version>${slf4j-api.version}</version>
        </dependency>
        <dependency>
          <groupId>org.slf4j</groupId>
          <artifactId>jcl-over-slf4j</artifactId>
          <version>${slf4j-api.version}</version>
          <scope>runtime</scope>
        </dependency>
    
        <!--用于与slf4j保持桥接 -->
        <dependency>
          <groupId>org.apache.logging.log4j</groupId>
          <artifactId>log4j-slf4j-impl</artifactId>
          <version>${log4j2.version}</version>
        </dependency>
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    ehcache.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
             updateCheck="false">
        <!--磁盘存储:将缓存中暂时不使用的对象,转移到硬盘,类似于Windows系统的虚拟内存-->
        <!--path:指定在硬盘上存储对象的路径-->
        <!--java.io.tmpdir 是默认的临时文件路径。 可以通过如下方式打印出具体的文件路径 System.out.println(System.getProperty("java.io.tmpdir"));-->
        <diskStore path="E://xxx"/>
    
    
        <!--defaultCache:默认的管理策略-->
        <!--eternal:设定缓存的elements是否永远不过期。如果为true,则缓存的数据始终有效,如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断-->
        <!--maxElementsInMemory:在内存中缓存的element的最大数目-->
        <!--overflowToDisk:如果内存中数据超过内存限制,是否要缓存到磁盘上-->
        <!--diskPersistent:是否在磁盘上持久化。指重启jvm后,数据是否有效。默认为false-->
        <!--timeToIdleSeconds:对象空闲时间(单位:秒),指对象在多长时间没有被访问就会失效。只对eternal为false的有效。默认值0,表示一直可以访问-->
        <!--timeToLiveSeconds:对象存活时间(单位:秒),指对象从创建到失效所需要的时间。只对eternal为false的有效。默认值0,表示一直可以访问-->
        <!--memoryStoreEvictionPolicy:缓存的3 种清空策略-->
        <!--FIFO:first in first out (先进先出)-->
        <!--LFU:Less Frequently Used (最少使用).意思是一直以来最少被使用的。缓存的元素有一个hit 属性,hit 值最小的将会被清出缓存-->
        <!--LRU:Least Recently Used(最近最少使用). (ehcache 默认值).缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存-->
        <defaultCache eternal="false" maxElementsInMemory="1000" overflowToDisk="false" diskPersistent="false"
                      timeToIdleSeconds="0" timeToLiveSeconds="600" memoryStoreEvictionPolicy="LRU"/>
    
    
        <!--name: Cache的名称,必须是唯一的(ehcache会把这个cache放到HashMap)-->
        <cache name="com.xlb.one.entity.User" eternal="false" maxElementsInMemory="100"
               overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="0"
               timeToLiveSeconds="300" memoryStoreEvictionPolicy="LRU"/>
    </ehcache>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    2.3 授权使用权限

    首先导入工具类==EhcacheUtil ==

    package com.xlb.ssm.shiro;
    
    import net.sf.ehcache.Cache;
    import net.sf.ehcache.CacheManager;
    import net.sf.ehcache.Element;
    
    import java.io.InputStream;
    
    public class EhcacheUtil {
    
        private static CacheManager cacheManager;
    
        static {
            try {
                InputStream is = EhcacheUtil.class.getResourceAsStream("/ehcache.xml");
                cacheManager = CacheManager.create(is);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    
        private EhcacheUtil() {
        }
    
        public static void put(String cacheName, Object key, Object value) {
            Cache cache = cacheManager.getCache(cacheName);
            if (null == cache) {
                //以默认配置添加一个名叫cacheName的Cache
                cacheManager.addCache(cacheName);
                cache = cacheManager.getCache(cacheName);
            }
            cache.put(new Element(key, value));
        }
    
    
        public static Object get(String cacheName, Object key) {
            Cache cache = cacheManager.getCache(cacheName);
            if (null == cache) {
                //以默认配置添加一个名叫cacheName的Cache
                cacheManager.addCache(cacheName);
                cache = cacheManager.getCache(cacheName);
            }
            Element element = cache.get(key);
            return null == element ? null : element.getValue();
        }
    
        public static void remove(String cacheName, Object key) {
            Cache cache = cacheManager.getCache(cacheName);
            cache.remove(key);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    测试类EhcacheDemo2

    package com.xlb.ssm.shiro;
    
    
    /**
     * 演示利用缓存存储数据
     * @author Administrator
     *
     */
    public class EhcacheDemo2 {
    	public static void main(String[] args) {
    		System.out.println(System.getProperty("java.io.tmpdir"));
    		EhcacheUtil.put("com.javaxl.four.entity.Book", 11, "zhangsan");
    		System.out.println(EhcacheUtil.get("com.javaxl.four.entity.Book", 11));
    
    		//EhcacheUtil.put("com.javaxl.one.entity.User", 11, "zhangsan");
    		//System.out.println(EhcacheUtil.get("com.javaxl.one.entity.User", 11));
    
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    现在读的是默认配置,因为没有该缓存,所有会新增一个
    在这里插入图片描述在这里插入图片描述

    缓存到指定目录E://xx
    现在是空的
    在这里插入图片描述修改配置文件ehcache.xml,改为true在这里插入图片描述运行测试文件
    在这里插入图片描述

    2.4 使用缓存

    先从缓冲中取,缓存中没有在查询数据库,查询数据放入缓存

    修改授权方法==MyRealm ==

    package com.xlb.ssm.shiro;
    
    import com.xlb.ssm.biz.UserBiz;
    import com.xlb.ssm.controller.ShiroUserController;
    import com.xlb.ssm.model.User;
    import org.apache.shiro.authc.AuthenticationException;
    import org.apache.shiro.authc.AuthenticationInfo;
    import org.apache.shiro.authc.AuthenticationToken;
    import org.apache.shiro.authc.SimpleAuthenticationInfo;
    import org.apache.shiro.authz.AuthorizationInfo;
    import org.apache.shiro.authz.SimpleAuthorizationInfo;
    import org.apache.shiro.realm.AuthorizingRealm;
    import org.apache.shiro.subject.PrincipalCollection;
    import org.apache.shiro.util.ByteSource;
    
    import java.util.Set;
    
    /**
     * @author 波哥
     * @QQ 2212371722
     * @company 波哥集团
     * @create  2022-08-25 18:27
     */
    public class MyRealm extends AuthorizingRealm {
    
        public UserBiz userBiz;
    
        public UserBiz getUserBiz() {
            return userBiz;
        }
    
        public void setUserBiz(UserBiz userBiz) {
            this.userBiz = userBiz;
        }
    
        /**
         * 授权
         * @param principals
         * @return
         * 替代shiro-web-ini
         */
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
            System.out.println("用户授权。。。。");
            //拿到授权用户
            String userName = principals.getPrimaryPrincipal().toString();//获取账户名
            // 给key取一个值 用户角色槽
            String cacheName1 = "user:role"+userName;
            //用户权限槽
            String cacheName2 = "user:role"+userName;
    
            //从缓冲中取角色
            Set<String> rolesByUserId = (Set<String>) EhcacheUtil.get(cacheName1, userName);
            //从缓冲中获取权限
            Set<String> persByUserId = (Set<String>) EhcacheUtil.get(cacheName1, userName);
            if(rolesByUserId == null || rolesByUserId.size()==0){
                rolesByUserId = userBiz.getRolesByUserId(userName);//查询角色
                System.out.println("从数据库中读取角色、、、");
                //放入缓存中
                EhcacheUtil.put(cacheName1,userName,rolesByUserId);
            }
            if(persByUserId == null || persByUserId.size()==0){
                //调用方法
                persByUserId = userBiz.getPersByUserId(userName);//查询权限
                System.out.println("从数据库中读取权限、、、");
                EhcacheUtil.put(cacheName2,userName,persByUserId);
            }
    
            //拿到授权器
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            //把当前用户权限交给Shiro的授权器(将当前登录的权限交给shiro授权器)
            info.setRoles(rolesByUserId);
            // 将当期登录的角色交给shiro授权期
            info.setStringPermissions(persByUserId);
            return info;
        }
    
    
        /**
         * 认证
         * @param token
         * @return
         * @throws AuthenticationException
         * 替代shiro.ini
         */
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
            //调用查询方法
            String userName = token.getPrincipal().toString();//获取账户名
            User user = userBiz.queryUserByName(userName);
            AuthenticationInfo info = new SimpleAuthenticationInfo(
                    user.getUsername(),
                    user.getPassword(),
                    ByteSource.Util.bytes(user.getSalt()),
                    this.getName()//realm的名字
            );
            return info;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100

    测试
    在这里插入图片描述

    这个效果大家可以自己利用debug调试看变化

  • 相关阅读:
    人工智能轨道交通行业周刊-第14期(2022.9.12-9.18)
    OpenWrt的内核启动分析
    Python Day7 字典和元组【零基础】
    八月份跳槽了,历经华为测开岗4轮面试,不出意外,被刷了...
    【ML】K-Means 聚类
    《Redis实战》笔记
    【微信小程序】父子组件的创建、通信与事件触发;组件生命周期
    QListWidget 类使用教程
    【Java】PAT(Basic Level) 1016 部分A+B
    qt5.15源码编译
  • 原文地址:https://blog.csdn.net/qq_63531917/article/details/126579832