最近已经快速入门了Shiro。对于登录、授权、认证等方法,每次都是从数据库直接查询。如果登录的人员过多,对数据库来说,是一项压力。如何减轻数据库的压力。
- EhCache 实现缓存
- 集成 Redis 实现 Shiro 缓存(推荐使用)
在此之前,我们已经简单学会EhCache 和Reids的使用。
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 # 数据库索引,默认为0host: 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值。