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

简介: 上一篇文章已经总结了使用ehCache来实现Shiro的缓存管理,步骤也很简单,引入依赖后,直接开启Realm的缓存管理器即可。如果使用Redis来实现缓存管理其实也是一样的,我们也是需要引入redis的依赖,然后开启缓存传入自定义的redis的缓存管理器就行。区别是我们需要为自定义的redis缓存管理器提供自定义的缓存管理类。这个缓存管理类中需要使用到redisTemplate模板,这个模板我们也是需要自己定义。

前言:

上一篇文章已经总结了使用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的缓存管理的所有功能。下面就来测试下上面的过程是否有效。


20210401104118382.gif


经过多次刷新页面发现,每次都是去缓存中拿数据,而不是去走认证方法,这样我们的缓存实现就成功了。但是上方的redisTemplate虽然是我们定义的,但是真实项目中一般不这么用,都会定义自己的Redis工具类。


shiro留下的坑,盐必须序列化


在Realm中定义缓存管理器与在安全管理器中定义缓存管理器有区别吗?


没什么区别,在自定义realm中设置缓存管理,与安全管理器中设置缓存,realm都能正常拥有缓存。不过不同的是若是在安全管理器中设置缓存,安全管理器拥有了缓存,realm同时也会有缓存,若是在realm设置,安全管理器则不会有缓存。但是事实上安全管理器的缓存管理也是为了realm服务的,除了realm需要缓存管理,其他也没有了。所以即使这设置的范围不同,但是效果都是一样的。在我看来,设置在realm中更符合逻辑。符合谁需要设置给谁的原则。


相关文章
|
8月前
|
缓存 NoSQL 关系型数据库
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
|
3月前
|
缓存 负载均衡 监控
135_负载均衡:Redis缓存 - 提高缓存命中率的配置与最佳实践
在现代大型语言模型(LLM)部署架构中,缓存系统扮演着至关重要的角色。随着LLM应用规模的不断扩大和用户需求的持续增长,如何构建高效、可靠的缓存架构成为系统性能优化的核心挑战。Redis作为业界领先的内存数据库,因其高性能、丰富的数据结构和灵活的配置选项,已成为LLM部署中首选的缓存解决方案。
|
4月前
|
存储 缓存 NoSQL
Redis专题-实战篇二-商户查询缓存
本文介绍了缓存的基本概念、应用场景及实现方式,涵盖Redis缓存设计、缓存更新策略、缓存穿透问题及其解决方案。重点讲解了缓存空对象与布隆过滤器的使用,并通过代码示例演示了商铺查询的缓存优化实践。
240 1
Redis专题-实战篇二-商户查询缓存
|
3月前
|
缓存 运维 监控
Redis 7.0 高性能缓存架构设计与优化
🌟蒋星熠Jaxonic,技术宇宙中的星际旅人。深耕Redis 7.0高性能缓存架构,探索函数化编程、多层缓存、集群优化与分片消息系统,用代码在二进制星河中谱写极客诗篇。
|
4月前
|
缓存 NoSQL 关系型数据库
Redis缓存和分布式锁
Redis 是一种高性能的键值存储系统,广泛用于缓存、消息队列和内存数据库。其典型应用包括缓解关系型数据库压力,通过缓存热点数据提高查询效率,支持高并发访问。此外,Redis 还可用于实现分布式锁,解决分布式系统中的资源竞争问题。文章还探讨了缓存的更新策略、缓存穿透与雪崩的解决方案,以及 Redlock 算法等关键技术。
|
8月前
|
缓存 NoSQL Java
Redis+Caffeine构建高性能二级缓存
大家好,我是摘星。今天为大家带来的是Redis+Caffeine构建高性能二级缓存,废话不多说直接开始~
1213 0
|
8月前
|
消息中间件 缓存 NoSQL
基于Spring Data Redis与RabbitMQ实现字符串缓存和计数功能(数据同步)
总的来说,借助Spring Data Redis和RabbitMQ,我们可以轻松实现字符串缓存和计数的功能。而关键的部分不过是一些"厨房的套路",一旦你掌握了这些套路,那么你就像厨师一样可以准备出一道道饕餮美食了。通过这种方式促进数据处理效率无疑将大大提高我们的生产力。
287 32
|
8月前
|
缓存 NoSQL Java
Redis:现代服务端开发的缓存基石与电商实践-优雅草卓伊凡
Redis:现代服务端开发的缓存基石与电商实践-优雅草卓伊凡
234 5
Redis:现代服务端开发的缓存基石与电商实践-优雅草卓伊凡
|
10月前
|
缓存 NoSQL Java
Redis应用—8.相关的缓存框架
本文介绍了Ehcache和Guava Cache两个缓存框架及其使用方法,以及如何自定义缓存。主要内容包括:Ehcache缓存框架、Guava Cache缓存框架、自定义缓存。总结:Ehcache适合用作本地缓存或与Redis结合使用,Guava Cache则提供了更灵活的缓存管理和更高的并发性能。自定义缓存可以根据具体需求选择不同的数据结构和引用类型来实现特定的缓存策略。
659 16
Redis应用—8.相关的缓存框架
|
存储 缓存 NoSQL
解决Redis缓存数据类型丢失问题
解决Redis缓存数据类型丢失问题
513 85