zoukankan      html  css  js  c++  java
  • Shiro结合Redis实现集群的Session共享

    完全参考:https://www.cnblogs.com/guitu18/p/11262106.html

    这主要用来做Shiro笔记用的

    Shiro结合Redis实现Session共享

    Shiro的登录也是基于Session的,默认情况下Session是保存在内存中。既然要做Session共享,那么肯定是将Session抽取出来,放到一个多个服务器都能访问到的地方。

    在集群环境下,我们仅仅需要继承AbstractSessionDAO,实现一下Session的增删改查等几个方法就可以很方便的实现Session共享,Shiro已经将完整的流程都做好了。这里涉及到的设计模式是模板方法模式,我们仅需要参与部分业务就可以完善整个流程了,当然我们不参与这部分流程的话,Shiro也有默认的实现方式,那就是将Session管理在当前应用的内存中。

    具体的Session管理(共享)怎么实现由我们自己决定,可以存放在数据库,也可以通过网络传输,甚至可以通过IO流写入文件都行,但就性能来讲,我们一般都将Session放入Redis中

    自定义RedisSessionDAO

    继承AbstractSessionDAO后实现Session增删改查的几个方法,然后再分布式系统中所有的项目再需要存储或获取Session时都会走Redis操作,这样就做到了集群环境的Session共享了。

    package com.qingfeng.springbootjspshiro.realm;
    
    import org.apache.shiro.session.Session;
    import org.apache.shiro.session.UnknownSessionException;
    import org.apache.shiro.session.mgt.eis.AbstractSessionDAO;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.stereotype.Component;
    
    import java.io.Serializable;
    import java.util.Collection;
    import java.util.concurrent.TimeUnit;
    
    /**
     * RedisSessionDao,以Redis持久化方式做Session共享,无需配置即可支持集群
     */
    @Component
    public class RedisSessionDao extends AbstractSessionDAO {
    
        @Value("${session.redis.expireTime}")
        private long expireTime;
    
        @Autowired
        private RedisTemplate redisTemplate;
    
        @Override
        protected Serializable doCreate(Session session) {
            Serializable sessionId = this.generateSessionId(session);
            this.assignSessionId(session, sessionId);
            redisTemplate.opsForValue().set(session.getId(), session, expireTime, TimeUnit.SECONDS);
            return sessionId;
        }
    
        @Override
        protected Session doReadSession(Serializable sessionId) {
            return sessionId == null ? null : (Session) redisTemplate.opsForValue().get(sessionId);
        }
    
        @Override
        public void update(Session session) throws UnknownSessionException {
            if (session != null && session.getId() != null) {
                session.setTimeout(expireTime * 1000);
                redisTemplate.opsForValue().set(session.getId(), session, expireTime, TimeUnit.SECONDS);
            }
        }
    
        @Override
        public void delete(Session session) {
            if (session != null && session.getId() != null) {
                redisTemplate.opsForValue().getOperations().delete(session.getId());
            }
        }
    
        @Override
        public Collection<Session> getActiveSessions() {
            return redisTemplate.keys("*");
        }
    
    }

    application.properties配置文件

    server.port=8888
    server.servlet.context-path=/shiro
    spring.application.name=shiro
    ###redis连接配置
    spring.redis.host=127.0.0.1
    spring.redis.port=6379
    spring.redis.password=123456
    ### Session过期时间(秒)
    session.redis.expireTime=3600

    注入RedisSessionDao

    上面只是我们自己实现的管理Session的方式,现在需要将其注入SessionManager中,并设置过期时间等相关参数。

       /**
         * Session超时时间(秒)
         */
        @Value("${session.redis.expireTime}")
        private long expireTime;
    
        @Bean("defaultWebSessionManager")
        public DefaultWebSessionManager defaultWebSessionManager(RedisSessionDao redisSessionDao) {
            DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
            sessionManager.setGlobalSessionTimeout(expireTime * 1000);
            sessionManager.setDeleteInvalidSessions(true);
            sessionManager.setSessionDAO(redisSessionDao);
            sessionManager.setSessionValidationSchedulerEnabled(true);
            sessionManager.setDeleteInvalidSessions(true);
            /**
             * 修改Cookie中的SessionId的key,默认为JSESSIONID,自定义名称
             */
            sessionManager.setSessionIdCookie(new SimpleCookie("JSESSIONID"));
            return sessionManager;
        }

    再将SessionManager注入Shiro的安全管理器SecurityManager中,前面说过,我们围绕安全相关的所有操作,都需要与SecurityManager打交道,这位才是Shiro中真正的老大哥。

        // 2、创建安全管理器
        @Bean("getSecurityManager")
        public DefaultWebSecurityManager getSecurityManager(@Qualifier("getRealm") Realm realm,RedisSessionDao redisSessionDao){
            DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
            //设置realm
            defaultWebSecurityManager.setRealm(realm);
            // 取消Cookie中的RememberMe参数
            defaultWebSecurityManager.setRememberMeManager(null);
            defaultWebSecurityManager.setSessionManager(defaultWebSessionManager(redisSessionDao));
            return defaultWebSecurityManager;
        }

    完整的ShiroConfig配置类

    package com.qingfeng.springbootjspshiro.config;
    
    import com.qingfeng.springbootjspshiro.realm.CustomerRealm;
    import com.qingfeng.springbootjspshiro.realm.RedisSessionDao;
    import org.apache.shiro.authc.AuthenticationException;
    import org.apache.shiro.authc.AuthenticationInfo;
    import org.apache.shiro.authc.AuthenticationToken;
    import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
    import org.apache.shiro.realm.CachingRealm;
    import org.apache.shiro.realm.Realm;
    import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
    import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
    import org.apache.shiro.web.servlet.SimpleCookie;
    import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * 用来整合shiro框架的相关配置
     */
    @Configuration
    public class ShiroConfig {
    
    
        // 1、创建shiroFilter      负责拦截所有请求
        @Bean
        public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getSecurityManager")DefaultWebSecurityManager securityManager){
            //创建shiro的filter
            ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
            //注入安全管理器
            shiroFilterFactoryBean.setSecurityManager(securityManager);
            //配置系统的受限资源
            Map<String ,String> map = new HashMap<>();
            map.put("/user/login","anon");   //anon:设置为公共资源
            map.put("/user/register","anon");
            map.put("/register.jsp","anon");
    
            map.put("/**","authc"); //authc:表示请求这个资源需要认证和授权
    
            //配置系统的公共资源
    
            //默认认证界面路径--->login.jsp
            shiroFilterFactoryBean.setLoginUrl("/login.jsp");
            shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
            return shiroFilterFactoryBean;
        }
    
        /**
         * Session超时时间(秒)
         */
        @Value("${session.redis.expireTime}")
        private long expireTime;
    
        @Bean("defaultWebSessionManager")
        public DefaultWebSessionManager defaultWebSessionManager(RedisSessionDao redisSessionDao) {
            DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
            sessionManager.setGlobalSessionTimeout(expireTime * 1000);
            sessionManager.setDeleteInvalidSessions(true);
            sessionManager.setSessionDAO(redisSessionDao);
            sessionManager.setSessionValidationSchedulerEnabled(true);
            sessionManager.setDeleteInvalidSessions(true);
            /**
             * 修改Cookie中的SessionId的key,默认为JSESSIONID,自定义名称
             */
            sessionManager.setSessionIdCookie(new SimpleCookie("JSESSIONID"));
            return sessionManager;
        }
    
        // 2、创建安全管理器
        @Bean("getSecurityManager")
        public DefaultWebSecurityManager getSecurityManager(@Qualifier("getRealm") Realm realm,RedisSessionDao redisSessionDao){
            DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
            //设置realm
            defaultWebSecurityManager.setRealm(realm);
            // 取消Cookie中的RememberMe参数
            defaultWebSecurityManager.setRememberMeManager(null);
            defaultWebSecurityManager.setSessionManager(defaultWebSessionManager(redisSessionDao));
            return defaultWebSecurityManager;
        }
    
    
        // 3、创建自定义Realm
        @Bean("getRealm")
        public Realm getRealm(){
            CustomerRealm customerRealm = new CustomerRealm();
            //设置hashed凭证匹配器
            HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
            //设置md5加密
            credentialsMatcher.setHashAlgorithmName("md5");
            //设置散列次数
            credentialsMatcher.setHashIterations(1024);
            customerRealm.setCredentialsMatcher(credentialsMatcher);
            return customerRealm;
    
        }
    
    
    }

    这样Redis实现的Session共享就完成了

    测试:SessionController 

    package com.qingfeng.springbootjspshiro.controller;
    
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authz.annotation.RequiresRoles;
    import org.apache.shiro.subject.Subject;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class SessionController {
    
        //在业务逻辑中代码实现判断用户权限
        //http://localhost:8888/shiro/testSession
        @RequiresRoles(value = {"admin"})////用来判断角色  同时具有 admin
        @RequestMapping("testSession")
        public String testSession(){
            System.out.println("集群Session共享成功");
            return "集群Session共享成功";
        }
    }

    springboot项目有时候需要进行集群,这时候其实就是同一个项目只是端口不一致在使用命令行得时候,只需要跟得参数调整一下即可

     启动两个端口号为8888和端口号为8889的项目

    进行登录成功

    分别访问:http://localhost:8888/shiro/testSession和http://localhost:8889/shiro/testSession结果

     

    它们的JSESSIONID值都是aad59a0d-7705-4c25-a70c-412df3ed45c5
  • 相关阅读:
    做题记录
    关于有向图强连通分量的一点想法
    浅谈二分图匹配(未完)
    水题狂欢赛 (爬楼梯赛)题解(偏向自我反省)
    浅谈迭代加深(iddfs)
    浅谈单调队列优化
    [cqbzoj#10644]鱼肉炸弹题解
    树形背包[2/ 50] luogu [P1273]
    树形背包[1/ 50] luogu [P2015] (超级板)
    (树状数组)区间修改,区间查询
  • 原文地址:https://www.cnblogs.com/Amywangqing/p/14713455.html
Copyright © 2011-2022 走看看