2021年你还不会Shiro?----10.使用redis实现Shiro的缓存


前言:
上一篇文章已经总结了使用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中更符合逻辑。符合谁需要设置给谁的原则。

猜你喜欢

转载自blog.csdn.net/m0_46897923/article/details/115316935