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. JS中map()与forEach()的区别和用法

    相同点: 1.都是循环遍历数组中的每一项 2.每次执行匿名函数都支持三个参数,参数分别为item(当前每一项),index(索引值),arr(原数组) 3.匿名函数中的this都是指向window 4 ...

  2. Linux软件的安装

    yum -y groups install "GNOME Desktop"  安装桌面系统startx  安装完成后输入指令进入到桌面化指令 安装tomcat sudo yum i ...

  3. Ubuntu 16.04 安装 SVN-Client (RaabitVCS)

    1.添加源 sudo add-apt-repository ppa:rabbitvcs/ppa 2. 更新源 sudo apt-get update 3.安装依赖库 sudo apt-get inst ...

  4. R-package XML 安装失败及解决方式

    安装R-package XML遇到的问题和解决方式 这个问题已经困扰了我很久很久一直找不到解决之法,后来终于找到了! Fedora 27, R 3.5.0, libxml和libxml2以及开发包均已 ...

  5. VSCode 使用Settings Sync同步配置(最新版教程,非常简单)

    VSCode 使用Settings Sync同步配置(最新版教程,非常简单) 之前无意中听到有人说,vsCode最大的缺点就是每次换个电脑或者临时去个新环境,就要配置一下各种插件,好不麻烦,以至于面试 ...

  6. 提交中文数据乱码问题---web.xml

    前端时间,做了个纯springmvc框架的一个后台系统,遇到了不少问题.特别是编码问题,让我纠结了很久.每次ajax传入数据的时候需要将form中的数据先进行编码 encodeURI(AA); 利用a ...

  7. 快应用 吸顶 bug

    官网说, 加载页面时,所有元素的appear事件都会被触发一次.因此,需要过滤第一次的appear事件:  但是,即使设置了过滤,也无效 于是,我把show属性改成了if属性,问题就解决 如下图:  

  8. 你绝对不知道的head标签

    先来梳理一下本文的目录结构 现在我将详解head标签和它六个子标签的属性和作用,请耐心看完,会有很大的收获哦! 一.head标签 可选属性: 属性 值 描述 profile URL 一个由空格分隔的 ...

  9. Python数据类型详解——元组

    Python数据类型详解--元组 有时候我们的列表数据不想被别人修改时该怎么办? 此时,就可以使用元组来存放,元祖又称为只读列表,不能修改 定义方式:与列表类似,将列表的[]换成()即可. 特性: 1 ...

  10. 《快照读、当前读和MVCC》

    1.快照读 快照读是基于 MVCC 和 undo log 来实现的,适用于简单 select 语句,避免了幻读. 读已提交:一个事务内操作一条数据,可以查询到另一个已提交事务操作同一条数据的最新值.( ...