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. jQuery学习和知识点总结归纳

    jQuery目前在Web前端开发所占的比重越来越高,在我们jQuery学习和开发的过程中都会去使用.jQuery帮我们解决了浏览器之间JS一些不兼容的地方和简化了原生JS对DOM的操作.下面把PHP程 ...

  2. Java并发编程实战笔记—— 并发编程1

    1.如何创建并运行java线程 创建一个线程可以继承java的Thread类,或者实现Runnabe接口. public class thread { static class MyThread1 e ...

  3. spring架构解析--入门一

    Spring 框架中的核心组件只有三个:Core.Context 和 Beans.它们构建起了整个 Spring 的骨骼架构.简单理解: spring core是工具,context是环境,而bean ...

  4. BFS-迷宫问题

    问题描述 定义一个二维数组: int maze[5][5] = { 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, ...

  5. 【I'm Telling the Truth】【HDU - 3729】 【匈牙利算法,DFS】

    思路 题意:该题主要说几个同学分别说出自己的名次所处区间,最后输出可能存在的未说谎的人数及对应的学生编号,而且要求字典序最大. 思路:刚刚接触匈牙利算法,了解的还不太清楚,附一个专门讲解匈牙利算法的博 ...

  6. Python机器学习之数据探索可视化库yellowbrick

    # 背景介绍 从学sklearn时,除了算法的坎要过,还得学习matplotlib可视化,对我的实践应用而言,可视化更重要一些,然而matplotlib的易用性和美观性确实不敢恭维.陆续使用过plot ...

  7. listary的使用心得

    1.关键字里面的 web 不仅仅可以打开网页也可以打开电脑上的应用程序. 2.但是呢,有时候虽然 URL 指向了正确的 exe 但是却打不开相应的软件.这是为什么呢?(有其他方法可以打开) 我在这里提 ...

  8. Mybatis-Spring扫描路径有重叠导致Invalid bound statement(not found)问题

    背景 近日,某个系统的测试环境mybatis总是报Invalid bound statement(not found)异常,导致tomcat容器无法启动.异常信息如下: org.apache.ibat ...

  9. ajax方法请求成功后,没有执行success的方法

    $.ajax( { type: "POST", url: "AddSupplier.aspx", dataType:"text", data ...

  10. C#/Java 动态生成电子发票

    电子发票是电商时代的产物,PDF发票是最常见的电子发票之一.在这篇文章中,我将给大家分享一个免费的动态生成PDF电子发票的C#方案,并在文章末尾附上Java解决方案. 典型的发票包含客户和供应商的名称 ...