前言:
上一篇文章已经总结了使用ehCache来实现Shiro的缓存管理,步骤也很简单,引入依赖后,直接开启Realm的缓存管理器即可。如果使用Redis来实现缓存管理其实也是一样的,我们也是需要引入redis的依赖,然后开启缓存传入自定义的redis的缓存管理器就行。区别是我们需要为自定义的redis缓存管理器提供自定义的缓存管理类。这个缓存管理类中需要使用到redisTemplate模板,这个模板我们也是需要自己定义。
一.实现过程
主要过程就是导入依赖、提供自定义缓存管理器、提供缓存管理类、开启缓存传入自定义缓存管理器即可。
1.导入redis依赖。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
2.提供自定义缓存管理器 MyRedisCacheManager
我们使用EhCacheManager缓存时发现EhCacheManager实现了CacheManager这个接口,所以我们自定义缓存管理器时也需要实现该接口,这个接口只有一个方法,就是用来获取缓存管理类,实现如下:
public class MyRedisCacheManager implements CacheManager { /** * 只要加入了缓存管理器,配置了缓存管理类,系统就会默认在查询完认证和授权后将信息放入到缓存中 * 且下次需要认证和授权时,都是优先去查询缓存中的内容,查询不到,才会去查询数据库,这里也验证了 * 这一点,与之前的画的加入缓存后的授权信息的获取图是一样的。 */ @Override public <K, V> Cache<K, V> getCache(String cacheName) throws CacheException { System.out.println("进入到了自定义缓存管理器,传入参数cacheName:"+ cacheName); return new RedisCache<K,V>(cacheName); } }
3.设计自己的缓存管理类
根据上方的代码我们可以看到,所有的缓存管理类的实例都应该是Cache的实现类。所以我们自己定义redis的缓存管理类应该也必须去实现这个Cache类。实现如下:
@NoArgsConstructor public class RedisCache<k,v> implements Cache<k,v> { private String cacheName; @Autowired public RedisTemplate redisTemplate; public static RedisTemplate redisTemplateSelf; @PostConstruct public void getRedisTemplate(){ this.redisTemplateSelf = redisTemplate; } public RedisCache (String cacheName1){ this.cacheName = cacheName1; } @Override public v get(k k) throws CacheException { System.out.println(cacheName+":获取缓存方法,传入参数:" + k+",此时的redisTemplate:"+redisTemplateSelf); //获取缓存中数据时一定要为k加toStirng方法,否则会报错序列化的错 if(null != redisTemplateSelf.opsForHash().get(cacheName.toString(),k.toString())){ return (v)redisTemplateSelf.opsForHash().get(cacheName.toString(),k.toString()); } return null; } @Override public v put(k k, v v) throws CacheException { System.out.println("加入缓存方法,传入参数 K:" + k+",V:"+v); //放入redis中的值,一定要是序列化的对象 redisTemplateSelf.opsForHash().put(cacheName.toString(),k.toString(),v); return null; } @Override public v remove(k k) throws CacheException { System.out.println("调用了remove方法,传入参数:"+k.toString()); redisTemplateSelf.opsForHash().delete(cacheName.toString(),k.toString()); return null; } @Override public void clear() throws CacheException { System.out.println("调用了clear方法"); redisTemplateSelf.delete(cacheName); } @Override public int size() { return redisTemplateSelf.opsForHash().size(cacheName).intValue(); } @Override public Set<k> keys() { return redisTemplateSelf.opsForHash().keys(cacheName); } @Override public Collection<v> values() { return redisTemplateSelf.opsForHash().values(cacheName); } }
这个类里我们实现了设置缓存、获取缓存、移除缓存、情况缓存等方法。
其中我们开启缓存后,用户登录成功就会将缓存放入到redis中,使用退出功能,就会清楚当前登录的缓存信息,授权信息也是一样,只要使用退出功能就会清空当前的缓存信息。但是这里并没有设计过期时间的处理。所以真实场景下,我们还需要考虑过期时间的设置。这里显然我们用到了RedisTemplate模板,这个模板我们一般也是自己定义,不过也可以直接使用SpringBoot默认提供的。这里我们采用自己定义RedisTemplate的方式。
4.自定义RedisTemplate注入到Spring容器中
//注入自定义的RedisTemplate @Bean public RedisTemplate getRedisTemplate(RedisConnectionFactory connectionFactory){ RedisTemplate redisTemplate = new RedisTemplate(); redisTemplate.setConnectionFactory(connectionFactory); redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); redisTemplate.afterPropertiesSet(); return redisTemplate; } //注入Shiro的缓存对象 @Bean public RedisCache getRedisCache(){ RedisCache redisCache = new RedisCache(); return redisCache; }
5.Realm中开启缓存管理
经过以上的过程,必要的过程以及完成了,可以在Realm中开启缓存管理器了,如下:
@Bean public FirstRealm getRealm(){ FirstRealm firstRealm = new FirstRealm(); HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(); hashedCredentialsMatcher.setHashAlgorithmName("MD5"); hashedCredentialsMatcher.setHashIterations(2048); // 下面的写法也是正常的,不过现在都是使用上面的写法,下面的写法已经是不推荐使用的了。 // Md5CredentialsMatcher md5CredentialsMatcher = new Md5CredentialsMatcher(); // md5CredentialsMatcher.setHashIterations(2048); firstRealm.setCredentialsMatcher(hashedCredentialsMatcher); //开启缓存 // firstRealm.setCachingEnabled(true);//开启全局的缓存管理 firstRealm.setAuthenticationCachingEnabled(true);//开启认证缓存 // firstRealm.setAuthorizationCachingEnabled(true);//开启授权缓存 //缓存名称很有必要设置,因为若是只使用k,v的形式设置redis缓存,认证和授权默认的k都是用户名,所以我们 //需要使用k,map的形式存储,k就可以是这个设置的缓存名称,缓存管理器中传入的k就是这个值。 firstRealm.setAuthenticationCacheName("authenticationCache");//设置缓存名称--认证 firstRealm.setAuthorizationCacheName("authorizationCache");//设置缓存名称--授权 firstRealm.setCacheManager(new MyRedisCacheManager()); return firstRealm; }
6.测试缓存是否有效
我们已经完成了使用Redis进行Shiro的缓存管理的所有功能。下面就来测试下上面的过程是否有效。
经过多次刷新页面发现,每次都是去缓存中拿数据,而不是去走认证方法,这样我们的缓存实现就成功了。但是上方的redisTemplate虽然是我们定义的,但是真实项目中一般不这么用,都会定义自己的Redis工具类。
shiro留下的坑,盐必须序列化
在Realm中定义缓存管理器与在安全管理器中定义缓存管理器有区别吗?
没什么区别,在自定义realm中设置缓存管理,与安全管理器中设置缓存,realm都能正常拥有缓存。不过不同的是若是在安全管理器中设置缓存,安全管理器拥有了缓存,realm同时也会有缓存,若是在realm设置,安全管理器则不会有缓存。但是事实上安全管理器的缓存管理也是为了realm服务的,除了realm需要缓存管理,其他也没有了。所以即使这设置的范围不同,但是效果都是一样的。在我看来,设置在realm中更符合逻辑。符合谁需要设置给谁的原则。