一、前言

在Shiro中加入缓存可以使权限相关操作尽可能快,避免频繁访问数据库获取权限信息,因为对于一个用户来说,其权限在短时间内基本是不会变化的。Shiro提供了Cache的抽象,其并没有直接提供相应的实现,因为这已经超出了一个安全框架的范围。在Shiro中可以集成常用的缓存实现,这里介绍基于Redis和Ehcache缓存的实现。

《Spring-Boot-shiro权限控制》 中,当用户访问”获取用户信息”、”新增用户”和”删除用户”的时候,后台输出了三次打印信息,如下所示:

用户wno704获取权限-----ShiroRealm.doGetAuthorizationInfo
用户wno704获取权限-----ShiroRealm.doGetAuthorizationInfo
用户wno704获取权限-----ShiroRealm.doGetAuthorizationInfo

说明在这三次访问中,Shiro都会从数据库中获取用户的权限信息,通过Druid数据源SQL监控后台也可以证实这一点:

这对数据库来说是没必要的消耗。接下来使用缓存来解决这个问题。

二、Redis缓存

2.1 引入Redis依赖

网络上已经有关于Shiro集成Redis的实现,我们引入即可:

1
2
3
4
5
6
        <!-- https://mvnrepository.com/artifact/org.crazycake/shiro-redis -->
<dependency>
<groupId>org.crazycake</groupId>
<artifactId>shiro-redis</artifactId>
<version>3.3.1</version>
</dependency>

2.2 配置Redis

我们在application.yml配置文件中加入Redis配置:

1
2
3
4
5
6
7
8
9
10
11
12
spring:
redis:
host: 127.0.0.1
port: 10007
password:
jedis:
pool:
max-active: 8
max-wait: -1
max-idle: 8
min-idle: 0
timeout: 0

2.3 配置Redis

接着在ShiroConfig中配置Redis:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
    @Value("${spring.redis.host}")
private String redisHost;
@Value("${spring.redis.port}")
private String redisPost;
@Value("${spring.redis.timeout}")
private int redisTimeout;
@Value("${spring.redis.password}")
private String redisPassword;

public RedisManager redisManager() {
RedisManager redisManager = new RedisManager();
redisManager.setHost(redisHost+":"+redisPost);
redisManager.setTimeout(redisTimeout);
if(!(redisPassword==null||redisPassword.equals(""))){
redisManager.setPassword(redisPassword);
}
return redisManager;
}

public RedisCacheManager cacheManager() {
RedisCacheManager redisCacheManager = new RedisCacheManager();
redisCacheManager.setRedisManager(redisManager());
return redisCacheManager;
}

2.4 注入RedisManager

上面代码配置了RedisManager,并将其注入到了RedisCacheManager中,最后在SecurityManager中加入RedisCacheManager:

1
2
3
4
5
6
7
@Bean
public SecurityManager securityManager(){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
...
securityManager.setCacheManager(cacheManager());
return securityManager;
}

2.5 测试

配置完毕启动项目,分别访问访问”获取用户信息”、”新增用户”和”删除用户”,可发现后台只打印一次获取权限信息:

用户wno704获取权限-----ShiroRealm.doGetAuthorizationInfo

查看Druid数据源SQL监控:

三、Ehcache

3.1 Ehcache依赖

1
2
3
4
5
6
        <!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-ehcache -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.6.0</version>
</dependency>

3.2 Ehcache配置

在src/main/resource/config路径下新增一个Ehcache配置——shiro-ehcache.xml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
updateCheck="false">
<diskStore path="java.io.tmpdir/Tmp_EhCache" />
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="false"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120" />

<!-- 登录记录缓存锁定1小时 -->
<cache name="passwordRetryCache"
maxEntriesLocalHeap="2000"
eternal="false"
timeToIdleSeconds="3600"
timeToLiveSeconds="0"
overflowToDisk="false"
statistics="true" />
</ehcache>

3.3 ShiroConfig配置Ehcache

接着在ShiroConfig中注入Ehcache缓存:

1
2
3
4
5
6
@Bean
public EhCacheManager getEhCacheManager() {
EhCacheManager em = new EhCacheManager();
em.setCacheManagerConfigFile("classpath:config/shiro-ehcache.xml");
return em;
}

3.4 注入EhCacheManager

将缓存对象注入到SecurityManager中:

1
2
3
4
5
6
7
8
@Bean  
public SecurityManager securityManager(){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(shiroRealm());
securityManager.setRememberMeManager(rememberMeManager());
securityManager.setCacheManager(getEhCacheManager());
return securityManager;
}

3.5 测试

配置完毕启动项目,分别访问访问”获取用户信息”、”新增用户”和”删除用户”,可发现后台只打印一次获取权限信息:

用户wno704获取权限-----ShiroRealm.doGetAuthorizationInfo

查看Druid数据源SQL监控:

SQL只执行了一次,说明缓存成功。