SpringBoot集成shiro使用Redis实现缓存机制

SpringBoot集成shiro使用Redis实现缓存机制

解决MD5+盐值加密反序列化失败的问题

  1. 首先需要实现一个RedisCacheManager类即CacheManager,可以实现CacheManager接口,具体代码如下:

    package com.dk.config.shiro.cache;
    
    import org.apache.shiro.cache.Cache;
    import org.apache.shiro.cache.CacheException;
    import org.apache.shiro.cache.CacheManager;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    /**
     * 自定义shiro缓存管理器
     * @author MJH on 2021/6/1.
     * @version 1.0
     */
    public class RedisCacheManager implements CacheManager {
    
        //获取日志
        private Logger logger = LoggerFactory.getLogger(this.getClass());
    
        //参数1:认证或者是授权缓存的统一名称
        @Override
        public <K, V> Cache<K, V> getCache(String cacheName) throws CacheException 	  {
            logger.info("输出的cacheName是什么呢???================"+cacheName);
            return new RedisCache<K, V>(cacheName);
        }
    }
    
  2. RedisManager需要返回一个Cache类型的数据,这里我们需要自定义实现一个RedisCache类,可以实现Cache接口,具体代码如下:

    package com.dk.config.shiro.cache;
    
    import com.dk.util.function.ApplicationContextUtils;
    import com.sun.org.apache.regexp.internal.RE;
    import org.apache.shiro.cache.Cache;
    import org.apache.shiro.cache.CacheException;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
    import org.springframework.data.redis.serializer.StringRedisSerializer;
    import org.springframework.web.context.ContextLoader;
    import org.springframework.web.context.WebApplicationContext;
    import org.springframework.web.context.support.WebApplicationContextUtils;
    
    import java.util.Collection;
    import java.util.Set;
    
    
    /**
     * 自定义redis缓存的实现
     * @author MJH on 2021/6/1.
     * @version 1.0
     */
    
    public class RedisCache<K, V> implements Cache<K, V> {
    
        private String cacheName;
        //获取日志
        private Logger logger = LoggerFactory.getLogger(this.getClass());
    
        public RedisCache() {
        }
    
        public RedisCache(String cacheName) {
            this.cacheName = cacheName;
        }
    
        @Override
        public V get(K k) throws CacheException {
            logger.info("从"+this.cacheName+"中get key:" + k);
            return (V) getRedisTemplate().opsForHash().get(this.cacheName, k.toString());
        }
    
        @Override
        public V put(K k, V v) throws CacheException {
            logger.info("向"+this.cacheName+"中put key:"+k + "     put value:"+k);
            getRedisTemplate().opsForHash().put(this.cacheName, k.toString(), v);
            return null;
        }
    
        @Override
        public V remove(K k) throws CacheException {
            logger.info("退出时执行的是remove函数");
            return (V) getRedisTemplate().opsForHash().delete(this.cacheName, k.toString());
        }
    
        @Override
        public void clear() throws CacheException {
            getRedisTemplate().delete(this.cacheName);
        }
    
        @Override
        public int size() {
            return getRedisTemplate().opsForHash().size(this.cacheName).intValue();
        }
    
        @Override
        public Set<K> keys() {
            return getRedisTemplate().opsForHash().keys(this.cacheName);
        }
    
        @Override
        public Collection<V> values() {
            return getRedisTemplate().opsForHash().values(this.cacheName);
        }
    
        private RedisTemplate getRedisTemplate(){
            RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtils.getBean("redisTemplate");
            redisTemplate.setKeySerializer(new StringRedisSerializer());
            redisTemplate.setHashKeySerializer(new StringRedisSerializer());
            //redisconfig中将值序列化为Jackson了,但是这种序列化存储shiro的信息的话,反序列化会失败,所以这里单独设置下
            redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
            redisTemplate.setHashValueSerializer(new JdkSerializationRedisSerializer());
            return redisTemplate;
        }
    }
    
  3. 在ShiroConfig中UserRealm中设置下,具体代码如下:

    //创建Realm对象, 需要自定义类:1
        @Bean
        public UserRealm userRealm(@Qualifier("credentialsMatcher") HashedCredentialsMatcher credentialsMatcher){
            UserRealm userRealm = new UserRealm();
            userRealm.setCredentialsMatcher(credentialsMatcher);  //设置加密方式
    
            //开启缓存管理
            //userRealm继承的AuthorizingRealm中默认开启了授权缓存,修改缓存名需要setCacheManager之前设置后,关闭也是
            userRealm.setAuthorizationCacheName("authorizationCache");  //设置授权缓存名称
            userRealm.setCacheManager(new RedisCacheManager());
            userRealm.setCachingEnabled(true);  //开启全局缓存
            userRealm.setAuthenticationCachingEnabled(true);  //开启认证缓存
            userRealm.setAuthenticationCacheName("authenticationCache");
    
            return userRealm;
        }
    
  4. 进行了以上操作后就实现了用redis作为shiro的缓存实现机制,但是如果你存储的密码是MD5+盐值加密,在序列化和反序列化时会出现问题,这是因为你的盐值没有序列化的原因,修改认证时生成盐值ByteSource credentialsSalt = ByteSource.Util.bytes(user.getUsername());ByteSource credentialsSalt = new MyByteSurce(user.getUsername());,其中反序列的话问题是因为生成盐值的函数中没有默认的空构造函数,供反序列化时使用,所以,这里要自己实现一个MyByteSurce来生成序列化后的盐值,继承Serializable,且里面要包含空构造函数。,具体代码如下:

    package com.dk.config.shiro.salt;
    
    import org.apache.shiro.codec.Base64;
    import org.apache.shiro.codec.CodecSupport;
    import org.apache.shiro.codec.Hex;
    import org.apache.shiro.util.ByteSource;
    import org.apache.shiro.util.SimpleByteSource;
    
    import java.io.File;
    import java.io.InputStream;
    import java.io.Serializable;
    import java.util.Arrays;
    
    /**
     * 自定义salt实现 实现序列化接口
     * @author MJH on 2021/6/1.
     * @version 1.0
     */
    public class MyByteSurce implements ByteSource, Serializable {
    
        private byte[] bytes;
        private String cachedHex;
        private String cachedBase64;
    
        //这个是空构造函数
        public MyByteSurce(){
    
        }
    
        public MyByteSurce(byte[] bytes) {
            this.bytes = bytes;
        }
    
        public MyByteSurce(char[] chars) {
            this.bytes = CodecSupport.toBytes(chars);
        }
    
        public MyByteSurce(String string) {
            this.bytes = CodecSupport.toBytes(string);
        }
    
        public MyByteSurce(ByteSource source) {
            this.bytes = source.getBytes();
        }
    
        public MyByteSurce(File file) {
            this.bytes = (new MyByteSurce.BytesHelper()).getBytes(file);
        }
    
        public MyByteSurce(InputStream stream) {
            this.bytes = (new MyByteSurce.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 byte[] getBytes() {
            return this.bytes;
        }
    
        public boolean isEmpty() {
            return this.bytes == null || this.bytes.length == 0;
        }
    
        public String toHex() {
            if (this.cachedHex == null) {
                this.cachedHex = Hex.encodeToString(this.getBytes());
            }
    
            return this.cachedHex;
        }
    
        public String toBase64() {
            if (this.cachedBase64 == null) {
                this.cachedBase64 = Base64.encodeToString(this.getBytes());
            }
    
            return this.cachedBase64;
        }
    
        public String toString() {
            return this.toBase64();
        }
    
        public int hashCode() {
            return this.bytes != null && this.bytes.length != 0 ? Arrays.hashCode(this.bytes) : 0;
        }
    
        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);
            }
        }
    }
    

​ 以上就是用redis实现shiro的缓存机制的代码,遇到问题多百度,总有符合解决自己问题的结果。

特别说明:以上代码是看bilibili上狂神说java和编程不良人两个up的视频进行学习的。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot是一个用于快速开发Java应用程序的开源框架,Shiro是一个强大且易于使用的Java安全框架,Redis是一个开源的内存数据库。结合使用这些技术可以实现单点登录功能。 在Spring Boot中使用Shiro来处理认证和授权,可以通过配置Shiro的Realm来实现用户的登录认证和权限控制。将用户的信息存储在Redis中,利用Redis的持久化特性来实现用户登录状态的共享和存储。 首先,在Spring Boot项目的配置文件中配置Redis的连接信息,以便连接到Redis数据库。 然后,创建一个自定义的Shiro的Realm,在其中重写认证和授权的方法。在认证方法中,将用户的登录信息存储到Redis中,以便其他服务可以进行验证。在授权方法中,根据用户的角色和权限进行相应的授权操作。 接着,在Spring Boot项目的配置类中配置Shiro的相关设置,包括Realm、Session管理器、Cookie管理器等。 最后,可以在Controller层中使用Shiro的注解来标记需要进行认证和授权的接口,以确保只有登录后且具备相应权限的用户才能访问这些接口。 总的来说,通过使用Spring Boot、ShiroRedis的组合,可以实现单点登录的功能。用户在登录后,将登录信息存储到Redis中,其他服务可以通过验证Redis中的数据来判断用户的登录状态。同时,Shiro提供了强大的认证和授权功能,可以确保只有具备相应权限的用户才能访问受保护的接口。这些功能的具体实现可以通过深入研究Spring Boot、ShiroRedis的源码来了解。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值