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:认证或者是授权缓存的统一名称@Overridepublic <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;}@Overridepublic V get(K k) throws CacheException {logger.info("从"+this.cacheName+"中get key:" + k);return (V) getRedisTemplate().opsForHash().get(this.cacheName, k.toString());}@Overridepublic 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;}@Overridepublic V remove(K k) throws CacheException {logger.info("退出时执行的是remove函数");return (V) getRedisTemplate().opsForHash().delete(this.cacheName, k.toString());}@Overridepublic void clear() throws CacheException {getRedisTemplate().delete(this.cacheName);}@Overridepublic int size() {return getRedisTemplate().opsForHash().size(this.cacheName).intValue();}@Overridepublic Set<K> keys() {return getRedisTemplate().opsForHash().keys(this.cacheName);}@Overridepublic 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@Beanpublic 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的视频进行学习的。


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部