【Shiro】3.Springboot实现缓存

最近已经快速入门了Shiro。对于登录、授权、认证等方法,每次都是从数据库直接查询。如果登录的人员过多,对数据库来说,是一项压力。如何减轻数据库的压力。

  • EhCache 实现缓存
  • 集成 Redis 实现 Shiro 缓存(推荐使用)

在此之前,我们已经简单学会EhCacheReids的使用。

EhCache 实现缓存

Shiro 提供了缓存管理器,这样在用户第一次认证授权后访问其受限资源的时候就不用每次查询数据库从而达到减轻数据压力的作用,使用 Shiro 的缓存管理器也很简单。第一步,在pom.xml引入依赖

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-ehcache</artifactId>
    <version>1.13.10</version>
</dependency>

ShiroConfiguratin 添加缓存配置:

 1 /**
 2  * 3.创建自定义realm
 3  * @return
 4  */
 5 @Bean
 6 public Realm getRealm(){
 7     UserRealm userRealm = new UserRealm();
 8     // 设置缓存管理器
 9     userRealm.setCacheManager(new EhCacheManager());
10     // 开启全局缓存
11     userRealm.setCachingEnabled(true);
12     // 开启认证缓存并指定缓存名称
13     userRealm.setAuthenticationCachingEnabled(true);
14     userRealm.setAuthenticationCacheName("authenicationCache");
15     // 开启缓存授权并指定缓存名称
16     userRealm.setAuthorizationCachingEnabled(true);
17     userRealm.setAuthorizationCacheName("authenicationCache");
18     return userRealm;
19 }

这样就将 EhCache 集成进来了,但是 shiro 的这个缓存是本地缓存,也就是说当程序宕机重启后仍然需要从数据库加载数据,不能实现分布式缓存的功能。

集成 Redis 实现 Shiro 缓存(推荐使用)

已知Redis的使用,我们实现了Redis的引用和配置。

在pom.xml文件引入Redis依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <version>2.5.15</version>
</dependency>

配置RedisConfig

 1 @Configuration
 2 @EnableCaching
 3 public class RedisConfig extends CachingConfigurerSupport {
 4 
 5     /**
 6      * RedisTemplate相关配置
 7      * 使redis支持插入对象
 8      *
 9      * @param factory
10      * @return 方法缓存 Methods the cache
11      */
12     @Bean
13     public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
14         RedisTemplate<String, Object> template = new RedisTemplate<>();
15         // 配置连接工厂
16         template.setConnectionFactory(factory);
17         // 序列化 key 和hashKey采用String序列化;value和hashValue采用JSON序列化
18         GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
19         template.setKeySerializer(RedisSerializer.string());
20         template.setHashKeySerializer(RedisSerializer.string());
21         // value和hashvalue采用JSON序列化
22         template.setValueSerializer(jsonRedisSerializer);
23         template.setHashValueSerializer(jsonRedisSerializer);
24         return template;
25     }
26 }

配置redis的数据库连接,这里以application.yml为例

spring:
  redis:
    database: 0     # 数据库索引,默认为0
    host: 127.0.0.1 # redis地址
    port: 6379      # redis服务器端口地址 
    # sping.redis.password 表示密码,因为密码为空,这里不设置
    jedis:
      pool:
        max-wait: 3000  # 连接池最大阻塞等待时间,单位毫秒(使用负值表示没有限制)
        min-idle: 0     # 连接池最小空闲时间
    timeout: 3000       # 连接超时时间(毫秒)

添加自定义缓存配置。自定义配置缓存管理器(RedisCacheManage)和自定义缓存方法(RedisCache)。

 1 @Autowired
 2 private RedisTemplate redisTemplate;
 3 
 4 /**
 5  * 3.创建自定义realm
 6  * @return
 7  */
 8 @Bean
 9 public Realm getRealm(){
10     SysUserRealm sysUserRealm = new SysUserRealm();
11     // 设置缓存管理器
12     sysUserRealm.setCacheManager(new RedisCacheManage(redisTemplate)); // 自定义Redis数据库缓存方法
13     // 开启全局缓存
14     sysUserRealm.setCachingEnabled(true);
15     // 开启认证缓存并指定缓存名称
16     sysUserRealm.setAuthenticationCachingEnabled(true);
17     sysUserRealm.setAuthenticationCacheName("authenicationCache");
18     // 开启缓存授权并指定缓存名称
19     sysUserRealm.setAuthorizationCachingEnabled(true);
20     sysUserRealm.setAuthorizationCacheName("authenicationCache");
21     return sysUserRealm;
22 }
 1 public class RedisCacheManage implements CacheManager {
 2     private RedisTemplate redisTemplate;
 3 
 4     public RedisCacheManage(RedisTemplate redisTemplate) {
 5          this.redisTemplate = redisTemplate;
 6     }
 7 
 8     @Override
 9     public <K, V> Cache<K, V> getCache(String s) throws CacheException {
10         return new RedisCache(s , this.redisTemplate);
11     }
12 }
 1 public class RedisCache<K,V> implements Cache<K,V> {
 2     /** 缓存名称 **/
 3     private String cacheName;
 4     private RedisTemplate redisTemplate;
 5     public RedisCache(String cacheName, RedisTemplate redisTemplate){
 6         this.cacheName = cacheName;
 7         this.redisTemplate = redisTemplate;
 8     }
 9 
10     /**
11      * 获取缓存
12      * @param k
13      * @return
14      * @throws CacheException
15      */
16     @Override
17     public V get(K k) throws CacheException {
18         System.out.println("cacheName=" +this.cacheName);
19         System.out.println("k=" + k.toString());
20         System.out.println("res=" + this.redisTemplate);
21         return (V) redisTemplate.opsForHash().get(this.cacheName,k.toString());
22     }
23 
24     /**
25      * 设置缓存Key
26      * @param k
27      * @param v
28      * @return
29      * @throws CacheException
30      */
31     @Override
32     public V put(K k, V v) throws CacheException {
33          redisTemplate.opsForHash().put(this.cacheName,k.toString(),v);
34          return null;
35     }
36 
37     /**
38      * 移除缓存Key
39      * @param k
40      * @return
41      * @throws CacheException
42      */
43     @Override
44     public V remove(K k) throws CacheException {
45         return (V)redisTemplate.opsForHash().delete(this.cacheName,k.toString());
46     }
47 
48     /**
49      * 清除缓存
50      * @throws CacheException
51      */
52     @Override
53     public void clear() throws CacheException {
54         redisTemplate.delete(this.cacheName);
55     }
56 
57     /**
58      * 获取缓存的数量
59      * @return
60      */
61     @Override
62     public int size() {
63         return redisTemplate.opsForHash().size(this.cacheName).intValue();
64     }
65 
66     @Override
67     public Set<K> keys() {
68         return (Set<K>) redisTemplate.opsForHash().values(this.cacheName);
69     }
70 
71     @Override
72     public Collection<V> values() {
73         return redisTemplate.opsForHash().values(this.cacheName);
74     }
75 }

 此时,我们可以运行测试方法(这里使用了测试接口)

 /**
     * 系统认证、授权测试
     * @return
     */
    @GetMapping("/test")
    public String test(){
        System.out.println("系统认证测试");
        String username = "christy";
        String password = Md5Utils.Md5PassWord("123456");
        System.out.println(password);
        try {
            Subject subject = SecurityUtils.getSubject();
            subject.login(new UsernamePasswordToken(username,password));
        }catch (UnknownAccountException e){
            System.err.println("认证失败:用户不存在");
            return  "redirect:/login";
        }catch (IncorrectCredentialsException e){
            System.err.println("认证失败:密码不正确");
            return  "redirect:/login";
        }catch (Exception e){
            System.err.println("认证失败");
            e.printStackTrace();
            // 如果认证失败仍然回到登录页面
            return  "redirect:/login";
        }
        return  "redirect:/login";
    }

调用接口后,我们发现Redis数据库存储了登录和授权的hash值。

 

posted @ 2024-10-05 10:55  陆陆无为而治者  阅读(12)  评论(0编辑  收藏  举报