美文网首页
SpringBoot + Shiro 整合

SpringBoot + Shiro 整合

作者: Made0107 | 来源:发表于2018-04-27 10:22 被阅读0次

    --
    本文记录了 SpringBoot + Shiro整合,包括 Shiro 登录认证(多个 Realm认证),Session、Cache + Redis共享,RememberMe(记住我)等多项功能。
    --

    一、Pom.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>com.made</groupId>
        <artifactId>ecifms</artifactId>  
        <version>0.0.1-SNAPSHOT</version> 
        <packaging>war</packaging> 
    
        <name>SpringBootTest</name>
        <description>Demo project for Spring Boot</description>
    
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>1.5.9.RELEASE</version>
            <relativePath /> <!-- lookup parent from repository -->
        </parent>
    
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
            <java.version>1.7</java.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
    
            <!-- oracle -->
            <dependency>
                <groupId>myOracle</groupId>
                <artifactId>ojdbc6</artifactId>
                <version>11.2.0</version>
                <type>jar</type>
            </dependency>
    
            
            <!-- redis -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-redis</artifactId>
                <version>1.4.5.RELEASE</version>
            </dependency>
         
            <!-- shiro spring. -->
            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-core</artifactId>
                <version>1.4.0</version>
            </dependency>
            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-spring</artifactId>
                <version>1.4.0</version>
            </dependency>
            <!-- shiro ehcache -->
            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-ehcache</artifactId>
                <version>1.4.0</version>
            </dependency>
    
            <!-- 热启动 热部署 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-devtools</artifactId>
                <optional>true</optional>
            </dependency>
    
            <!-- tomcat 的支持 可以访问跳转页面 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-tomcat</artifactId>
                <scope>provided</scope>
            </dependency>
    
            <!-- 支持访问jsp页面 -->
            <dependency>
                <groupId>org.apache.tomcat.embed</groupId>
                <artifactId>tomcat-embed-jasper</artifactId>
                <scope>provided</scope>
            </dependency>
            <dependency>
                <groupId>javax.servlet.jsp.jstl</groupId>
                <artifactId>jstl-api</artifactId>
                <version>1.2</version>
            </dependency>
            
            <!--mybatis -->
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>1.3.1</version>
            </dependency>
            <!--mapper -->
            <dependency>
                <groupId>tk.mybatis</groupId>
                <artifactId>mapper-spring-boot-starter</artifactId>
                <version>1.1.4</version>
            </dependency>
            <!--pagehelper -->
            <dependency>
                <groupId>com.github.pagehelper</groupId>
                <artifactId>pagehelper-spring-boot-starter</artifactId>
                <version>1.2.1</version>
            </dependency>
            
    
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <!-- 热部署 热启动 -->
                    <configuration>
                        <fork>true</fork>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    
        <!--私有仓库,即私服 -->
         <repositories>
            <repository>
                <id>public</id><!-- 这个id需要与你的组的group ID一致 -->
                <name>Public Repository</name>
                <url>http://10.1.164.8:15000/nexus/content/groups/public</url>
            </repository>
        </repositories> 
    
    </project>
    

    二、Redis配置

    1. application.properties

    #配置哨兵
    #spring.redis.database=0  
    #spring.redis.password=made
    spring.redis.pool.max-idle=8
    spring.redis.pool.min-idle=0
    spring.redis.pool.max-active=8
    spring.redis.pool.max-wait=-1
    #哨兵监听redis server名称  
    spring.redis.sentinel.master=mymaster
    #哨兵的配置列表  
    spring.redis.sentinel.nodes=10.1.164.11:26379,10.1.164.12:26379
    

    2. RedisConfig.java

    package com.made.common.config;
    
    import java.util.HashSet;
    import java.util.Set;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import redis.clients.jedis.JedisSentinelPool;
    
    
    @Configuration
    public class RedisConfig {
        //注入集群节点的信息
    
        /*@Value("${spring.redis.pool.max-idle}")
        private int maxIdle;
    
        @Value("${spring.redis.pool.min-idle}")
        private int minIdle;
    
        @Value("${spring.redis.pool.max-active}")
        private String maxActive;
    
        @Value("${spring.redis.pool.max-wait}")
        private long maxWait;*/
    
        @Value("${spring.redis.password}")
        private String password;
    
        @Value("${spring.redis.sentinel.master}")
        private String master;
    
    
        @Value("${spring.redis.sentinel.nodes}")
        private String nodes;
    
    
    
        @Bean  //<bean>
        public JedisSentinelPool getJedisSentinelPool(){
            //分割集群节点
            String[] cNodes = nodes.split(",");
            //创建set集合
            Set<String> nodes = new HashSet<String>();
            //循环集群节点对象
            for (String node : cNodes) {
                //String[] hp = node.split(":");
                nodes.add(node);
                System.out.println(node);
            }
    
            /*JedisPoolConfig poolConfig = new JedisPoolConfig();
    
            poolConfig.setMaxIdle(maxIdle);
            poolConfig.setMinIdle(minIdle);
            poolConfig.setMaxWaitMillis(maxWait);*/
    
            //JedisSentinelPool jedisSentinelPool = new JedisSentinelPool(masterName, nodes, poolConfig, password);
            
            JedisSentinelPool jedisSentinelPool = new JedisSentinelPool(master, nodes, password);
            return jedisSentinelPool;
        }
    }
    

    3. RedisService.java

    package com.made.common.redis;
    
    import java.util.Set;
    import java.util.concurrent.TimeUnit;
    
    import javax.annotation.Resource;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.data.redis.core.StringRedisTemplate;
    import org.springframework.data.redis.core.ValueOperations;
    import org.springframework.stereotype.Component;
    
    import com.made.common.CommonDefinitionUtil;
    
    import redis.clients.jedis.Jedis;
    import redis.clients.jedis.JedisSentinelPool;
    
    @Component
    public class RedisService {
    
        private static Logger logger = LoggerFactory.getLogger(RedisService.class);
    
        @Autowired
        private JedisSentinelPool jedisPool;
    
        /**
         * get value from redis
         * @param key
         * @return
         */
        public byte[] get(byte[] key){
            byte[] value = null;
            Jedis jedis = jedisPool.getResource();
            try{
                value = jedis.get(key);
            } catch(Exception e) {
    
                logger.error("redis key:{} get value occur exception", new String(key));
    
            } finally{
                jedis.close();
            }
            return value;
        }
    
        /**
         * set 
         * @param key
         * @param value
         * @return
         */
        public byte[] set(byte[] key,byte[] value){
            Jedis jedis = jedisPool.getResource();
            try{
                jedis.set(key,value);
    
                jedis.expire(key, (int) CommonDefinitionUtil.SHIRO_SESSION_TIME);
    
            } catch(Exception e) {
    
                logger.error("redis key:{} set value:{} occur exception", new String(key), new String(value));
    
            } finally{
                jedis.close();
            }
            return value;
        }
    
        /**
         * set 
         * @param key
         * @param value
         * @param expire
         * @return
         */
        public byte[] set(byte[] key,byte[] value,int expire){
            Jedis jedis = jedisPool.getResource();
            try{
                jedis.set(key,value);
                if(expire != 0){
                    jedis.expire(key, expire);
                }
            } catch(Exception e) {
    
                logger.error("redis key:{} set value:{} in expire:{} occur exception", 
                        new String(key), new String(value), expire);
    
            } finally{
                jedis.close();
            }
            return value;
        }
    
        /**
         * del
         * @param key
         */
        public void del(byte[] key){
            Jedis jedis = jedisPool.getResource();
            try{
                jedis.del(key);
            } catch(Exception e) {
    
                logger.error("redis key:{} del value occur exception", new String(key));
    
            } finally{
                jedis.close();
            }
        }
    
        /**
         * flush
         */
        public void flushDB(){
            Jedis jedis = jedisPool.getResource();
            try{
                jedis.flushDB();
            } catch(Exception e) {
    
                logger.error("redis flushDB occur exception");
    
            } finally{
                jedis.close();
            }
        }
    
        /**
         * size
         */
        public Long dbSize(){
            Long dbSize = 0L;
            Jedis jedis = jedisPool.getResource();
            try{
                dbSize = jedis.dbSize();
            } catch(Exception e) {
    
                logger.error("redis get dbSize occur exception");
    
            } finally{
                jedis.close();
            }
            return dbSize;
        }
    
        /**
         * keys
         * @param regex
         * @return
         */
        public Set<byte[]> keys(String pattern){
            Set<byte[]> keys = null;
            Jedis jedis = jedisPool.getResource();
            try{
                keys = jedis.keys(pattern.getBytes());
            } catch(Exception e) {
    
                logger.error("redis get keys in pattern:{} occur exception", pattern);
    
            } finally{
                jedis.close();
            }
            return keys;
        }
    
        /**
         * 判断key是否存在
         */
        public Boolean exists(String key) {
            Jedis jedis = jedisPool.getResource();
            Boolean result = jedis.exists(key);
            jedis.close();
            return result;
        }
    
        /**
         * 设置失效时间
         */
        public Long expire(String key, int seconds) {
            Jedis jedis = jedisPool.getResource();
            Long result = jedis.expire(key, seconds);
            jedis.close();
            return result;
        }
    
        /**
         * key-value 存数据到redis
         */
        public String set(String key, String value) {
            Jedis jedis = jedisPool.getResource();
            String result = jedis.set(key, value);
            jedis.close();
            return result;
        }
    
        /**
         * 根据key,获取值
         */
        public String get(String key) {
            Jedis jedis = jedisPool.getResource();
            String result = jedis.get(key);
            jedis.close();
            return result;
        }
    
        /**
         * 根据key,删除数据
         */
        /*public Long del(String key) {
            Jedis jedis = jedisPool.getResource();
            Long result = jedis.del(key);
            jedis.close();
            return result;
        }*/
    }
    

    三、Realm认证(多个Realm认证)

    1. UsernamePasswordTokenCustom.java

    首先需要继承 UsernamePasswordToken类,新增一个属性,用来标识登录类型。

    package com.made.common.shiro.token;
    
    import org.apache.shiro.authc.UsernamePasswordToken;
    
    
    /**
     * 自定义  UsernamePasswordToken
     * @author majunde
     *
     */
    public class UsernamePasswordTokenCustom extends UsernamePasswordToken{
    
        /**
         * 
         */
        private static final long serialVersionUID = 1L;
        
        /**
         * 登录类型: 普通登录,SSO登录
         */
        private String loginType;
        
    
        /**
         * 构造函数
         * @param username 
         * @param password
         * @param loginType 登录类型
         */
        public UsernamePasswordTokenCustom(String username, String password, String loginType) {
            super(username, password);
            
            this.loginType = loginType;
        }
    
        public UsernamePasswordTokenCustom(String username, String password, String loginType, boolean rememberMe) {
            super(username, password, rememberMe);
            // TODO Auto-generated constructor stub
            this.loginType = loginType;
        }
        
    
        public UsernamePasswordTokenCustom(String username, String password, boolean rememberMe) {
            super(username, password, rememberMe);
            // TODO Auto-generated constructor stub
        }
    
        public UsernamePasswordTokenCustom(String username, String password) {
            super(username, password);
            // TODO Auto-generated constructor stub
        }
    
        public String getLoginType() {
            return loginType;
        }
    
        public void setLoginType(String loginType) {
            this.loginType = loginType;
        }
    
    }
    

    2. ModularRealmAuthenticatorCustom.java

    其次,还需要继承与ModularRealmAuthenticator,重写其中的
    doAuthenticate(AuthenticationToken authenticationToken)方法。

    package com.made.common.shiro.realm;
    
    import java.util.ArrayList;
    import java.util.Collection;
    
    import org.apache.shiro.authc.AuthenticationException;
    import org.apache.shiro.authc.AuthenticationInfo;
    import org.apache.shiro.authc.AuthenticationToken;
    import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
    import org.apache.shiro.realm.Realm;
    
    import com.made.common.shiro.token.UsernamePasswordTokenCustom;
    
    
    /**
     * 自定义  ModularRealmAuthenticator
     *  由该类 决定 分配调用 那个 realm, 默认全部调用
     * @author majunde
     *
     */
    public class ModularRealmAuthenticatorCustom extends ModularRealmAuthenticator{
    
        
        @Override
        protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken)
                throws AuthenticationException {
            // TODO Auto-generated method stub
            
            // 判断 getRealms(); 是否为空
            assertRealmsConfigured();
            
            Collection<Realm> realms = getRealms();
            
            // 获取 token 得到 loginType
            UsernamePasswordTokenCustom token = (UsernamePasswordTokenCustom) authenticationToken;
            
            String loginType = token.getLoginType();
            
            Collection<Realm> typeRealms = new ArrayList<>();
            
            for (Realm realm : realms) {
                
                // 如果 loginTyppe为空,代表默认
                if (loginType == null) {  
    
                    if (realm.getName().contains("Default")) {
                        
                        typeRealms.add(realm);
                        break;
                    }
                    continue;
                }
                
                if (realm.getName().contains(loginType)) {
                    
                    typeRealms.add(realm);
                    break;
                }
            }
            
            if (typeRealms.size() == 1) {
                
              return doSingleRealmAuthentication(typeRealms.iterator().next(), authenticationToken);
            }
            // 改变 realms --> typeRealms
            return doMultiRealmAuthentication(typeRealms, authenticationToken);
        }
    }
    
    

    3.LoginTypeEnum.java

    另外,为了标识 登录类型,使用了枚举来记录用户的登录类型。

    package com.made.common.shiro.token;
    
    /**
     * 登录类型
     * @author majunde
     *
     */
    public enum LoginTypeEnum {
    
        /**
         * 默认登录
         */
        Default("Default"), 
        
        /**
         * SSO 登录
         */
        Sso("Sso"); 
        
        private final String value;
    
        private LoginTypeEnum(String value) {
            this.value = value;
        }
    
        public String getValue() {
            return value;
        }
        
    }
    

    4.实现

    相关文章

      网友评论

          本文标题:SpringBoot + Shiro 整合

          本文链接:https://www.haomeiwen.com/subject/ugfdwxtx.html