Shiro功能应用(七)--Shiro集成Redis缓存(shiro-redis3.1.0)

     如果单机,使用EHCache就可以的,单如果多节点部署时就不行了,本文主要将Shiro和Redis缓存集成,在上一篇文章Shiro功能应用(六)–登陆失败重试次数控制代码基础进行添加Redis缓存。

代码实现:

      代码地址:
          https://github.com/OooooOz/SpringBoot-Shiro

     首先将ShiroConfig关于EHCache的和SessionManager的配置去掉。

     ShiroConfig的安全管理器SecurityManager:

  @Bean(name="securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("shiroRealm") MyShiroRealm shiroRealm){
		... ... ... ...
		securityManager.setCacheManager(redisCacheManager());		    //配置 redis缓存管理器
        securityManager.setSessionManager(redisSessionManager());		//配置 redissession管理
        return securityManager;
    }

     ShiroConfig的Redis缓存管理器:

	//import org.crazycake.shiro.RedisCacheManager;包的RedisCacheManager的对象
    @Bean("redisCacheManager")
    public RedisCacheManager redisCacheManager(){
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager());
        return redisCacheManager;
    }

     ShiroConfig的Redis配置管理器:

    @Bean
    public RedisManager redisManager(){
        RedisManager redisManager = new RedisManager();
        redisManager.setHost("192.168.2.104");
        redisManager.setPort(6379);
        //redisManager.setPassword("123456");
        return redisManager;
    }

     ShiroConfig的Redis会话管理器:

@Bean("redisSessionManager")
    public SessionManager redisSessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        Collection<SessionListener> listeners = new ArrayList<SessionListener>();
        //配置监听
        listeners.add(sessionListener());
        sessionManager.setSessionListeners(listeners);
        sessionManager.setSessionIdCookie(sessionIdCookie());
        sessionManager.setSessionDAO(redisSessionDAO());
        sessionManager.setCacheManager(redisCacheManager());
        //sessionManager.setGlobalSessionTimeout(60000);    //全局会话超时时间(单位毫秒),默认30分钟  暂时设置为10秒钟 用来测试
        sessionManager.setDeleteInvalidSessions(true);
        //取消url 后面的 JSESSIONID
        sessionManager.setSessionIdUrlRewritingEnabled(false);
        return sessionManager;

    }

     ShiroConfig的redisSessionDao:

    @Bean("redisSessionDAO")
    public SessionDAO redisSessionDAO() {
        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
        redisSessionDAO.setRedisManager(redisManager());
        redisSessionDAO.setExpire(3000);//session在redis中的保存时间,最好大于session会话超时时间,单位s
        return redisSessionDAO;
    }

     自定义Realm类修改认证方法返回的info:

		SimpleAuthenticationInfo authenticationInfo = 
//				new SimpleAuthenticationInfo(user,user.getPassword(),ByteSource.Util.bytes(user.getSalt()),"shiroRealm");
		new SimpleAuthenticationInfo(user,user.getPassword(),new MySimpleByteSource(user.getSalt()),"shiroRealm");

     新增的MySimpleByteSource类:

/**
 * 解决:
 *  shiro 使用缓存时出现:java.io.NotSerializableException: org.apache.shiro.util.SimpleByteSource
 *  序列化后,无法反序列化的问题
 */
public class MySimpleByteSource implements ByteSource, Serializable {
    private static final long serialVersionUID = 5175082362119580768L;

    private  byte[] bytes;
    private String cachedHex;
    private String cachedBase64;

    public MySimpleByteSource(){
    }

    public MySimpleByteSource(byte[] bytes) {
        this.bytes = bytes;
    }

    public MySimpleByteSource(char[] chars) {
        this.bytes = CodecSupport.toBytes(chars);
    }

    public MySimpleByteSource(String string) {
        this.bytes = CodecSupport.toBytes(string);
    }

    public MySimpleByteSource(ByteSource source) {
        this.bytes = source.getBytes();
    }

    public MySimpleByteSource(File file) {
        this.bytes = (new BytesHelper()).getBytes(file);
    }

    public MySimpleByteSource(InputStream stream) {
        this.bytes = (new BytesHelper()).getBytes(stream);
    }

    public static boolean isCompatible(Object o) {
        return o instanceof byte[] || o instanceof char[] || o instanceof String || o instanceof ByteSource || o instanceof File || o instanceof InputStream;
    }

    public void setBytes(byte[] bytes) {
        this.bytes = bytes;
    }

    @Override
    public byte[] getBytes() {
        return this.bytes;
    }


    @Override
    public String toHex() {
        if(this.cachedHex == null) {
            this.cachedHex = Hex.encodeToString(this.getBytes());
        }
        return this.cachedHex;
    }

    @Override
    public String toBase64() {
        if(this.cachedBase64 == null) {
            this.cachedBase64 = Base64.encodeToString(this.getBytes());
        }

        return this.cachedBase64;
    }

    @Override
    public boolean isEmpty() {
        return this.bytes == null || this.bytes.length == 0;
    }
    @Override
    public String toString() {
        return this.toBase64();
    }

    @Override
    public int hashCode() {
        return this.bytes != null && this.bytes.length != 0? Arrays.hashCode(this.bytes):0;
    }

    @Override
    public boolean equals(Object o) {
        if(o == this) {
            return true;
        } else if(o instanceof ByteSource) {
            ByteSource bs = (ByteSource)o;
            return Arrays.equals(this.getBytes(), bs.getBytes());
        } else {
            return false;
        }
    }

    private static final class BytesHelper extends CodecSupport {
        private BytesHelper() {
        }

        public byte[] getBytes(File file) {
            return this.toBytes(file);
        }

        public byte[] getBytes(InputStream stream) {
            return this.toBytes(stream);
        }
    }

主要问题:

     1).java.io.NotSerializableException: org.apache.shiro.util.SimpleByteSource异常:

          因为认证方法返回info对象第三个参数ByteSource类型,并没有实现序列化接口,所以序列化的时候出现异常,
          解决: 自定义一个类实现ByteSource和Serializable接口,见上文MySimpleByteSource

     2).com.demo.entity.User cannot be cast to com.demo.entity.User转换异常:
          一看,同样类型为什么转换不成呢?因为SpringBoot项目配了热部署。
          解决: 注释掉热部署依赖。Clear一下就好了。

<dependency>
   	<groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <optional>true</optional>
    <scope>true</scope>
</dependency>

     3). org.crazycake.shiro.exception.PrincipalInstanceException: class com.demo.entity.User must has getter for field: authCacheKey or id无法获取缓存中权限redisKey。
          因为shiro-redis3.1.0里面,有个条件写死的。本人的User类不满足条件。
在这里插入图片描述
     可见,User类要有AuthCacheKey或者Id属性,也就是要有getAuthCacheKey()或getId()方法,条件才能满足,下面会通过映射获取属性值。不满足就抛异常了。

          解决: 也行换个包可以解决,要么就修改User类,但改动会有点大,要么就换一种Redis的实现方式,也就是不用Shiro-Redis的包。可查看下一篇Shiro功能应用(八)–Shiro集成RedisTemplate(SDR)

     4).RedisCache才是主要操作redis缓存的操作类, 代码中任何出现操作缓存(get、put)之类的,都会到这个类操作

发布了17 篇原创文章 · 获赞 18 · 访问量 4486

猜你喜欢

转载自blog.csdn.net/weixin_43901882/article/details/105789945