2023-03-11  阅读(2)
原文作者:柒's Blog 原文地址:https://blog.52itstyle.vip/archives/1304/

202303111618024541.png

前言

前段时间做了一个图床的小项目,安全框架使用的是Shiro。为了使用户7x24小时访问,决定把项目由单机升级为集群部署架构。但是安全框架shiro只有单机存储的SessionDao,尽管Shrio有基于Ehcache-rmi的组播/广播实现,然而集群的分布往往是跨网段的,甚至是跨地域的,所以寻求新的方案。

架构

202303111618029692.png

方案

使用 redis 集中存储,实现分布式集群共享用户信息,这里我们采用第三方开源插件crazycake来实现,pom.xml 引入:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <dependency>
        <groupId>org.crazycake</groupId>
        <artifactId>shiro-redis</artifactId>
        <version>3.2.3</version>
    </dependency>

配置 application.properties

    # Redis
    # 数据库索引(默认为0)
    redis.database=0
    # 服务器地址 变更为自己的
    redis.host=127.0.0.1
    # 服务器连接端口
    redis.port=6379
    # 服务器连接密码,如果不设置密码注释掉即可
    # redis.password=
    # 连接超时时间(毫秒)
    redis.timeout=30000

本来crazycake插件已经实现了RedisManager,但是参数不可配,这里我们需要自己重写一下:

    public class RedisManager extends WorkAloneRedisManager implements IRedisManager {
    
        private RedisProperties redis;
    
        private JedisPool jedisPool;
    
        public RedisManager(RedisProperties redis) {
            this.redis = redis;
        }
    
        private void init() {
            synchronized(this) {
                if (this.jedisPool == null) {
                    this.jedisPool = new JedisPool(this.getJedisPoolConfig(), redis.getHost(), redis.getPort(),
                            redis.getTimeout(), redis.getPassword(), redis.getDatabase());
                }
            }
        }
    
        @Override
        protected Jedis getJedis() {
            if (this.jedisPool == null) {
                this.init();
            }
            return this.jedisPool.getResource();
        }
    }

参数配置 RedisProperties

    @Data
    @ConfigurationProperties(prefix = "redis")
    public class RedisProperties {
    
        private String host;
        private int port;
        private int timeout;
        private String password;
        private int database;
    }

配置 ShiroConfig

    /**
     * Shiro权限配置
     * 一定要配置 @Configuration 和 @EnableConfigurationProperties 注解
     */
    @Configuration
    @EnableConfigurationProperties({RedisProperties.class})
    public class ShiroConfig {
    
        private RedisProperties redis;
    
        public ShiroConfig(RedisProperties redis) {
            this.redis = redis;
        }
    
        @Bean
        public UserRealm userRealm() {
            return new UserRealm();
        }
    
        @Bean
        public ShiroFilterFactoryBean shiroFilterFactoryBean (SecurityManager securityManager) {
            ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
            shiroFilterFactoryBean.setSecurityManager(securityManager);
            shiroFilterFactoryBean.setLoginUrl("/index.html");
            shiroFilterFactoryBean.setUnauthorizedUrl("/403");
            // 拦截器
            Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
            /**
             * 静态文件
             */
            filterChainDefinitionMap.put("/file/**","anon");
            /**
             * 登录注册
             */
            filterChainDefinitionMap.put("/register.shtml","anon");
            filterChainDefinitionMap.put("/login.shtml","anon");
            /**
             * 管理后台
             */
            filterChainDefinitionMap.put("/sys/**", "roles[admin]");
            filterChainDefinitionMap.put("/**", "authc");
            shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
            return shiroFilterFactoryBean;
        }
    
        @Bean
        public SessionsSecurityManager securityManager() {
            DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
            securityManager.setRealm(userRealm());
            securityManager.setCacheManager(cacheManager());
            securityManager.setSessionManager(sessionManager());
            return securityManager;
        }
        @Bean
        public DefaultWebSessionManager sessionManager() {
            DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
            sessionManager.setSessionIdUrlRewritingEnabled(false);
            sessionManager.setSessionDAO(redisSessionDAO());
            return sessionManager;
        }
        @Bean
        public ShiroDialect shiroDialect(){
            return new ShiroDialect();
        }
    
        /**
         * cacheManager 缓存 redis实现
         * @return
         */
        public RedisCacheManager cacheManager() {
            RedisCacheManager redisCacheManager = new RedisCacheManager();
            redisCacheManager.setRedisManager(redisManager());
            return redisCacheManager;
        }
    
        /**
         * 配置shiro redisManager
         * @return
         */
        public RedisManager redisManager() {
            RedisManager redisManager = new RedisManager(redis);
            return redisManager;
        }
    
        /**
         * RedisSessionDAO shiro sessionDao层的实现
         * 原理就是重写 AbstractSessionDAO
         * 有兴趣的小伙伴自行阅读源码
         */
        @Bean
        public RedisSessionDAO redisSessionDAO() {
            RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
            redisSessionDAO.setRedisManager(redisManager());
            return redisSessionDAO;
        }
    }

小结

是不是很爽,以后重启应用再也不用担心用户投诉了?


Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。

它的内容包括:

  • 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
  • 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
  • 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
  • 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
  • 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
  • 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
  • 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
  • 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw

目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:

想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询

同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。

阅读全文