Shiro的Session缓存主要有两种方案,一种是使用Shiro自己的Session,不使用HttpSession,自己实现Shiro的Cache接口和Session缓存等;另外一种是直接使用spring boot的spring-session-data-redis的包,并且配置RedisTemplate和Redis的序列化方法就可以了。相对来说第二种方式非常简单,第一种还需要不少开发工作。下面主要来说第一种方式的思路。

Shiro的缓存

要缓存Session,最好要先集成Shiro的缓存。Shiro提供了类似于Spring的Cache抽象,即Shiro本身不实现Cache,但是对Cache进行了又抽象,方便更换不同的底层Cache实现。 Shiro提供了Cache接口和CacheManager接口,以及CacheManagerAware接口来注入CacheManager。

  • 实现Cache的缓存接口,shiro的Cache对进行Spring Cache包装
@SuppressWarnings("unchecked")
class SpringCacheWrapper <V> implements Cache<String, V> { private final String REDIS_SHIRO_CACHE = "shiro-cache#";
private org.springframework.cache.Cache springCache;
private CacheManager cacheManager; SpringCacheWrapper(CacheManager cacheManager, @NotNull org.springframework.cache.Cache springCache) {
this.springCache = springCache;
this.cacheManager = cacheManager;
} @Override
public V get(String key) throws CacheException {
ValueWrapper cacheValue = springCache.get(getCacheKey(key));
return (V) Optional.ofNullable(cacheValue).map(p->p.get()).orElse(null);
} @Override
public V put(String key, V value) throws CacheException {
springCache.put(getCacheKey(key), value);
return value;
} @Override
public V remove(String key) throws CacheException {
springCache.evict(getCacheKey(key));
return null;
} private String getCacheKey(String key) {
return REDIS_SHIRO_CACHE + key;
} @Override
public void clear() throws CacheException {
springCache.clear();
} @Override
public int size() {
throw new UnsupportedOperationException("invoke spring cache size method not supported");
} @Override
public Set<String> () {
throw new UnsupportedOperationException("invoke spring cache keys method not supported");
} @Override
public Collection<V> values() {
throw new UnsupportedOperationException("invoke spring cache values method not supported");
}
}

实现CacheManager的缓存接口,shiro的CacheManager对进行Spring CacheManager的包装

public class SpringCacheManagerWrapper implements CacheManager {

    private org.springframework.cache.CacheManager cacheManager;

    public SpringCacheManagerWrapper(org.springframework.cache.CacheManager cacheManager) {
this.cacheManager = cacheManager;
} @Override
public <K, V> Cache<K, V> getCache(String name) throws CacheException {
org.springframework.cache.Cache springCache = cacheManager.getCache(name);
return new SpringCacheWrapper(springCache);
}
}

Session的缓存

需要实现CacheSessionDAO接口,实现Session的缓存方法。

public class ShiroSessionDAO extends CachingSessionDAO {

    private Cache<String, Session> cache;

    public ShiroSessionDAO(CacheManager cacheManager) {
String cacheName = getActiveSessionsCacheName();
this.setCacheManager(cacheManager);
this.cache = getCacheManager().getCache(cacheName);
} @Override
protected void doUpdate(Session session) {
if(session==null) {
return;
}
cache.put(session.getId().toString(), session);
} @Override
protected void doDelete(Session session) {
if(session==null){
return;
}
cache.remove(session.getId().toString());
} @Override
protected Serializable doCreate(Session session) {
if(session == null) {
return null;
}
Serializable sessionId = this.generateSessionId(session);
assignSessionId(session, sessionId);
cache.put(sessionId.toString(), session);
return sessionId;
} @Override
protected Session doReadSession(Serializable sessionId) {
if(sessionId==null) {
return null;
} Session session=(Session) cache.get(sessionId.toString());
return session;
}
}

配置Bean

配置Redis

@Configuration
public class RedisConfiguration extends CachingConfigurerSupport { @Bean
public KeyGenerator keyGenerator() {
return new KeyGenerator() {
@Override
public Object generate(Object target, Method method, Object... params) {
StringBuilder sb = new StringBuilder();
sb.append(target.getClass().getName());
sb.append("#" + method.getName());
for (Object obj : params) {
sb.append(obj.toString());
}
return sb.toString();
}
};
} @SuppressWarnings({ "unchecked", "rawtypes" })
@Bean
public RedisCacheManager redisCacheManager(@Autowired RedisTemplate redisTemplate) {
// spring cache注解序列化配置
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
.serializeKeysWith(
RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.getKeySerializer()))
.serializeValuesWith(
RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.getValueSerializer()))
.disableCachingNullValues()
.entryTtl(Duration.ofSeconds(60)); Set<String> cacheNames = new HashSet<>();
cacheNames.add("user"); Map<String, RedisCacheConfiguration> configMap = new HashMap<>();
configMap.put("user", redisCacheConfiguration.entryTtl(Duration.ofSeconds(120))); RedisCacheManager redisCacheManager = RedisCacheManager.builder(redisTemplate.getConnectionFactory())
.cacheDefaults(redisCacheConfiguration).transactionAware().initialCacheNames(cacheNames)
.withInitialCacheConfigurations(configMap).build();
return redisCacheManager;
} @Bean
public RedisTemplate<Object, Object> redisTemplate(@Autowired RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<Object, Object>();
redisTemplate.setConnectionFactory(redisConnectionFactory); StringRedisSerializer stringSerializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(stringSerializer); // key序列化
redisTemplate.setHashKeySerializer(stringSerializer); // Hash key序列化 FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<Object>(Object.class);
redisTemplate.setValueSerializer(fastJsonRedisSerializer); // value序列化
redisTemplate.setHashValueSerializer(fastJsonRedisSerializer); // Hash value序列化
redisTemplate.afterPropertiesSet();
return redisTemplate;
} @Bean
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
} }

配置Shiro的缓存和CacheSessionDao

@Bean(name = "securityManager")
public org.apache.shiro.mgt.SecurityManager defaultWebSecurityManager(@Autowired UserRealm userRealm, @Autowired TokenRealm tokenValidateRealm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setAuthenticator(multiRealmAuthenticator());
securityManager.setRealms(Arrays.asList(userRealm, tokenValidateRealm));
securityManager.setRememberMeManager(rememberMeManager());
securityManager.setCacheManager(new SpringCacheManagerWrapper());
//必须使用DefaultWebSessionManager,不能是ServletContainerSessionManager
DefaultWebSessionManager webSessionManager = new DefaultWebSessionManager();
webSessionManager.setSessionDAO(cachingSessionDAO);
securityManager.setSessionManager(webSessionManager);
return securityManager;
}

总结

从上面的步骤可以看出配置Shiro的Session缓存,还是比较麻烦的。本来也是打算采用这种方式,后来在网上发现有个Session集成Redis的包,如下所示,也发现使用这种方式更简单,后来就直接使用Spring的Session了,只需要配置Redis(如上所示)就可以了。

 <dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>

Shiro使用Session缓存的更多相关文章

  1. 使用redis进行基于shiro的session集群共享

    之前写过一篇nginx多tomcat负载均衡,主要记录了使用nginx对多个tomcat 进行负载均衡,其实进行负载均衡之前还有一个问题没有解决,那就是集群间的session共享,不然用户在登录网站之 ...

  2. 细说shiro之七:缓存

    官网:https://shiro.apache.org/ 一. 概述 Shiro作为一个开源的权限框架,其组件化的设计思想使得开发者可以根据具体业务场景灵活地实现权限管理方案,权限粒度的控制非常方便. ...

  3. shiro源码篇 - shiro的session共享,你值得拥有

    前言 开心一刻 老师对小明说:"乳就是小的意思,比如乳猪就是小猪,乳名就是小名,请你用乳字造个句" 小明:"我家很穷,只能住在40平米的乳房" 老师:" ...

  4. Shiro - 关于session

    Shiro Session session管理可以说是Shiro的一大卖点. Shiro可以为任何应用(从简单的命令行程序还是手机应用再到大型企业应用)提供会话解决方案. 在Shiro出现之前,如果我 ...

  5. shiro 的session持久化

    对于分布式系统,一般都牵扯到Session共享问题,而想实现Session共享,就要实现Session的持久化操作,即是将内存中的Session持久化至缓存数据库. SessionDAO是Shiro提 ...

  6. ASP.NET Core 使用 Redis 和 Protobuf 进行 Session 缓存

    前言 上篇博文介绍了怎么样在 asp.net core 中使用中间件,以及如何自定义中间件.项目中刚好也用到了Redis,所以本篇就介绍下怎么样在 asp.net core 中使用 Redis 进行资 ...

  7. hibernate笔记--缓存机制之 一级缓存(session缓存)

    一级缓存: 又称为session缓存,它和session生命周期相同,周期非常短.是事务级别的缓存: 还是以Book和Category这两个表为例,我们用代码观察一个缓存的存在: 假设现在我要去查询i ...

  8. Hibernate的session缓存和对象的四种状态

    一.session缓存 说session缓存就得说到JAVA对象的生命周期,当没有任何引用指向一个对象时,对象则可以被gc回收,也就是生命周期结束了 而hibernate获取一个对象后,会将对象存入s ...

  9. hibernate中的session缓存

    1.什么是session缓存? 在 Session 接口的实现中包含一系列的 Java 集合, 这些 Java 集合构成了 Session 缓存. 只要 Session 实例没有结束生命周期, 且没有 ...

随机推荐

  1. MyISAM和InnoDB在索引上的差别及其它区别

    首先我们知道MyISM和InnoDB索引都是由B+树实现的,但在索引管理数据方式上却有所不同. InnoDB是聚集索引,数据文件是和(主键)索引绑在一起的,即索引 + 数据 = 整个表数据文件,通过主 ...

  2. 简洁实用Socket框架DotNettySocket

    目录 简介 产生背景 使用方式 TcpSocket WebSocket UdpSocket 结尾 简介 DotNettySocket是一个.NET跨平台Socket框架(支持.NET4.5+及.NET ...

  3. Eclipse高级操作 远程调试

    Eclipse高级操作 远程调试 JPDA是SUN JDK自带的远程调试机制.它提供了一套标准的调试接口,可以从虚拟机一级允许外界用特定协议探测虚拟机内部的运作细节.只要你装了JDK1.2以上的SUN ...

  4. android ——Intent

    Intent是android程序中各组件之间进行交互的重要方式,它可以用于指明当前组件想要执行的动作,也可以在不同组件之间传递数据,Intent一般被用于启动活动,启动服务以及发送广播. 一.显式的使 ...

  5. Postman系列二:Postman中get接口实战讲解(接口测试介绍,接口测试流程,头域操作)

    一:接口测试介绍 接口测试:就是针对软件对外提供服务的接口输入输出进行测试,以及接口间相互逻辑的测试,验证接口功能和接口描述文档的一致性. 接口测试好处:接口测试通常能对系统测试的更为彻底,更高的保障 ...

  6. 洛谷 P3338 [ZJOI2014]力

    题意简述 读入\(n\)个数\(q_i\) 设\(F_j = \sum\limits_{i<j}\frac{q_i\times q_j}{(i-j)^2 }-\sum\limits_{i> ...

  7. Linux 精确判断是否同一文件--及终端获取字符串md5 的值

    背景 今天发现一个同事用 文件大小 对比,来判断编译所得的一个可执行文件是不是同一个文件. 讲道理 这种方式出错的概率很低,但是用这样的方法,一旦出错就容易被坑一把狠的. 所以我来分享一下 md5 在 ...

  8. exlipse php 插件安装地址

    以前的exlipse PHP插件老是有问题,下面这个地址可以使用. http://www.phpsrc.org/eclipse/pti/

  9. 【数学+思维】ZZULIOJ 1531: 小L的区间求和

    题目链接 题目描述 在给定的一个整数序列中,小L希望找到一个连续的区间,这个区间的和能够被k整除,请你帮小L算一下满足条件的最长的区间长度是多少. 输入 第一行输入两个整数n.k.(1 <= n ...

  10. Linux下Kafka下载与安装教程

    原文链接:http://www.studyshare.cn/software/details/1176/0 一.预备环境 Kafka是java生态圈中的一员,运行在java虚拟机上,按Kafka官方说 ...