SpringBoot整合Shiro-session管理(六)

原文:https://blog.csdn.net/qq_34021712/article/details/80418112

Shiro提供了完整的企业级会话管理功能,不依赖于底层容器(如Tomcat),不管是J2SE还是J2EE环境都可以使用,提供了会话管理,会话事件监听,会话存储/持久化,容器无关的集群,失效/过期支持,对Web的透明支持,SSO单点登录的支持等特性。即直接使用 Shiro 的会话管理可以直接替换如 Web 容器的会话管理。

shiro中的session特性

  • 基于POJO/J2SE:shiro中session相关的类都是基于接口实现的简单的java对象(POJO),兼容所有java对象的配置方式,扩展也更方便,完全可以定制自己的会话管理功能 。
  • 简单灵活的会话存储/持久化:因为shiro中的session对象是基于简单的java对象的,所以你可以将session存储在任何地方,例如,文件,各种数据库,内存中等。
  • 容器无关的集群功能:shiro中的session可以很容易的集成第三方的缓存产品完成集群的功能。例如,Ehcache + Terracotta, Coherence, GigaSpaces等。你可以很容易的实现会话集群而无需关注底层的容器实现。
  • 异构客户端的访问:可以实现web中的session和非web项目中的session共享。
  • 会话事件监听:提供对对session整个生命周期的监听。
  • 保存主机地址:在会话开始session会存用户的ip地址和主机名,以此可以判断用户的位置。
  • 会话失效/过期的支持:用户长时间处于不活跃状态可以使会话过期,调用touch()方法,可以主动更新最后访问时间,让会话处于活跃状态。
  • 透明的Web支持:shiro全面支持Servlet 2.5中的session规范。这意味着你可以将你现有的web程序改为shiro会话,而无需修改代码。
  • 单点登录的支持:shiro session基于普通java对象,使得它更容易存储和共享,可以实现跨应用程序共享。可以根据共享的会话,来保证认证状态到另一个程序。从而实现单点登录。

会话相关API

Subject subject = SecurityUtils.getSubject();
Session session = subject.getSession();

与web中的 HttpServletRequest.getSession(boolean create) 类似!
Subject.getSession(true)。即如果当前没有创建session对象会创建一个;
Subject.getSession(false),如果当前没有创建session对象则返回null。

拿到session之后,就可以调用以下API

返回值方法名描述
ObjectgetAttribute(Object key)根据key标识返回绑定到session的对象
CollectiongetAttributeKeys()获取在session中存储的所有的key
StringgetHost()获取当前主机ip地址,如果未知,返回null
SerializablegetId()获取session的唯一id
DategetLastAccessTime()获取最后的访问时间
DategetStartTimestamp()获取session的启动时间
longgetTimeout()获取session失效时间,单位毫秒
voidsetTimeout(long maxIdleTimeInMillis)设置session的失效时间
ObjectremoveAttribute(Object key)通过key移除session中绑定的对象
voidsetAttribute(Object key, Object value)设置session会话属性
voidstop()销毁会话
voidtouch()更新会话最后访问时间

会话管理器

会话管理器管理着应用中所有Subject的会话的创建、维护、删除、失效、验证等工作。是Shiro的核心组件,顶层组件SecurityManager直接继承了SessionManager,且提供了SessionsSecurityManager实现直接把会话管理委托给相应的SessionManager,DefaultSecurityManager及DefaultWebSecurityManager默认SecurityManager都继承了SessionsSecurityManager。
SecurityManager提供了如下接口:

//启动会话
Session start(SessionContext context);

//根据会话Key获取会话
Session getSession(SessionKey key) throws SessionException;

另外用于Web环境的WebSessionManager又提供了如下接口:

//是否使用Servlet容器的会话
boolean isServletContainerSessions();

Shiro还提供了ValidatingSessionManager用于验资并过期会话:

//验证所有会话是否过期
void validateSessions();

在这里插入图片描述
Shiro提供了三个默认实现:
DefaultSessionManager:DefaultSecurityManager使用的默认实现,用于JavaSE环境;

ServletContainerSessionManager:DefaultWebSecurityManager使用的默认实现,用于Web环境,其直接使用Servlet容器的会话;

DefaultWebSessionManager:用于Web环境的实现,可以替代ServletContainerSessionManager,自己维护着会话,直接废弃了Servlet容器的会话管理。

shiro配置会话管理配置

创建session监听类,实现SessionListener接口
package com.springboot.test.shiro.config.shiro;

import org.apache.shiro.session.Session;
import org.apache.shiro.session.SessionListener;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author: wangsaichao
 * @date: 2018/5/15
 * @description: 配置session监听器
 */
public class ShiroSessionListener implements SessionListener{

    /**
     * 统计在线人数
     * juc包下线程安全自增
     */
    private final AtomicInteger sessionCount = new AtomicInteger(0);

    /**
     * 会话创建时触发
     * @param session
     */
    @Override
    public void onStart(Session session) {
        //会话创建,在线人数加一
        sessionCount.incrementAndGet();
    }

    /**
     * 退出会话时触发
     * @param session
     */
    @Override
    public void onStop(Session session) {
        //会话退出,在线人数减一
        sessionCount.decrementAndGet();
    }

    /**
     * 会话过期时触发
     * @param session
     */
    @Override
    public void onExpiration(Session session) {
        //会话过期,在线人数减一
        sessionCount.decrementAndGet();
    }
    /**
     * 获取在线人数使用
     * @return
     */
    public AtomicInteger getSessionCount() {
        return sessionCount;
    }
}

在ShiroConfig类中添加以下Bean

配置session监听
/**
 * 配置session监听
 * @return
 */
@Bean("sessionListener")
public ShiroSessionListener sessionListener(){
    ShiroSessionListener sessionListener = new ShiroSessionListener();
    return sessionListener;
}
配置会话ID生成器
/**
 * 配置会话ID生成器
 * @return
 */
@Bean
public SessionIdGenerator sessionIdGenerator() {
    return new JavaUuidSessionIdGenerator();
}
配置sessionDAO
/**
 * SessionDAO的作用是为Session提供CRUD并进行持久化的一个shiro组件
 * MemorySessionDAO 直接在内存中进行会话维护
 * EnterpriseCacheSessionDAO  提供了缓存功能的会话维护,默认情况下使用MapCache实现,内部使用ConcurrentHashMap保存缓存的会话。
 * @return
 */
@Bean
public SessionDAO sessionDAO() {
    EnterpriseCacheSessionDAO enterpriseCacheSessionDAO = new EnterpriseCacheSessionDAO();
    //使用ehCacheManager
    enterpriseCacheSessionDAO.setCacheManager(ehCacheManager());
    //设置session缓存的名字 默认为 shiro-activeSessionCache
    enterpriseCacheSessionDAO.setActiveSessionsCacheName("shiro-activeSessionCache");
    //sessionId生成器
    enterpriseCacheSessionDAO.setSessionIdGenerator(sessionIdGenerator());
    return enterpriseCacheSessionDAO;
}
配置sessionId的Cookie
/**
 * 配置保存sessionId的cookie 
 * 注意:这里的cookie 不是上面的记住我 cookie 记住我需要一个cookie session管理 也需要自己的cookie
 * @return
 */
@Bean("sessionIdCookie")
public SimpleCookie sessionIdCookie(){
    //这个参数是cookie的名称
    SimpleCookie simpleCookie = new SimpleCookie("sid");
    //setcookie的httponly属性如果设为true的话,会增加对xss防护的安全系数。它有以下特点:

    //setcookie()的第七个参数
    //设为true后,只能通过http访问,javascript无法访问
    //防止xss读取cookie
    simpleCookie.setHttpOnly(true);
    simpleCookie.setPath("/");
    //maxAge=-1表示浏览器关闭时失效此Cookie
    simpleCookie.setMaxAge(-1);
    return simpleCookie;
}
配置session管理器
/**
 * 配置会话管理器,设定会话超时及保存
 * @return
 */
@Bean("sessionManager")
public SessionManager sessionManager() {

    DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
    Collection<SessionListener> listeners = new ArrayList<SessionListener>();
    //配置监听
    listeners.add(sessionListener());
    sessionManager.setSessionListeners(listeners);
    sessionManager.setSessionIdCookie(sessionIdCookie());
    sessionManager.setSessionDAO(sessionDAO());
    sessionManager.setCacheManager(ehCacheManager());

    return sessionManager;

}

注意:这里的SessionIdCookie 是新建的一个SimpleCookie对象,不是之前整合记住我的那个rememberMeCookie 如果配错了,就会出现session经典问题:每次请求都是一个新的session 并且后台报以下异常,解析的时候报错.因为记住我cookie是加密的用户信息,所以报解密错误

org.apache.shiro.crypto.CryptoException: Unable to execute 'doFinal' with cipher instance [javax.crypto.Cipher@461df537].
将session管理器交给SecurityManager
/**
 * 配置核心安全事务管理器
 * @return
 */
@Bean(name="securityManager")
public SecurityManager securityManager() {
    DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();
    //设置自定义realm.
    securityManager.setRealm(shiroRealm());
    //配置记住我 参考博客:
    securityManager.setRememberMeManager(rememberMeManager());

    //配置 ehcache缓存管理器 参考博客:
    securityManager.setCacheManager(ehCacheManager());

    //配置自定义session管理,使用ehcache 或redis
    securityManager.setSessionManager(sessionManager());

    return securityManager;
}
启动测试

配置完成之后启动测试,登陆的时候点击 rememberMe 查看cookie 可以看到一个sessionId 和 一个记住我cookie

配置清理孤立session

以上整合会话管理,还有一个问题: 如果用户如果不点注销,直接关闭浏览器,不能够进行session的清空处理,所以为了防止这样的问题,还需要增加有一个会话的验证调度。
修改sessionManager如下:

/**
 * 配置会话管理器,设定会话超时及保存
 * @return
 */
@Bean("sessionManager")
public SessionManager sessionManager() {

    DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
    Collection<SessionListener> listeners = new ArrayList<SessionListener>();
    //配置监听
    listeners.add(sessionListener());
    sessionManager.setSessionListeners(listeners);
    sessionManager.setSessionIdCookie(sessionIdCookie());
    sessionManager.setSessionDAO(sessionDAO());
    sessionManager.setCacheManager(ehCacheManager());

    //全局会话超时时间(单位毫秒),默认30分钟  暂时设置为10秒钟 用来测试
    sessionManager.setGlobalSessionTimeout(1800000);
    //是否开启删除无效的session对象  默认为true
    sessionManager.setDeleteInvalidSessions(true);
    //是否开启定时调度器进行检测过期session 默认为true
    sessionManager.setSessionValidationSchedulerEnabled(true);
    //设置session失效的扫描时间, 清理用户直接关闭浏览器造成的孤立会话 默认为 1个小时
    //设置该属性 就不需要设置 ExecutorServiceSessionValidationScheduler 底层也是默认自动调用ExecutorServiceSessionValidationScheduler
    //暂时设置为 5秒 用来测试
    sessionManager.setSessionValidationInterval(3600000);

    return sessionManager;

}

测试方法:

设置 全局session超时时间setGlobalSessionTimeout为 10000 ,设置session的失效扫描时间setSessionValidationInterval为5000,然后测试
结果如下:

后台每5秒会打印日志 显示 如下:
2018/05/23 11:20:11.516 o.a.s.s.m.ExecutorServiceSessionValidationScheduler [] DEBUG Executing session validation...
2018/05/23 11:20:11.516 o.a.s.s.m.AbstractValidatingSessionManager [] INFO  Validating all active sessions...
2018/05/23 11:20:11.516 o.a.s.w.s.m.DefaultWebSessionManager [] DEBUG SessionKey argument is not HTTP compatible or does not have an HTTP request/response pair. Session ID cookie will not be removed due to invalidated session.
2018/05/23 11:20:11.516 o.a.s.s.m.AbstractValidatingSessionManager [] DEBUG Invalidated session with id [2e9e317f-7575-4bb0-98c4-3e6e5d2578f5] (expired)
2018/05/23 11:20:11.516 o.a.s.s.m.AbstractValidatingSessionManager [] INFO  Finished session validation.  [1] sessions were stopped.
2018/05/23 11:20:11.516 o.a.s.s.m.ExecutorServiceSessionValidationScheduler [] DEBUG Session validation completed successfully in 0 milliseconds.

session过期之后 再点击连接跳转到首页,并且后台报错 提示 找不到session

org.apache.shiro.session.UnknownSessionException: There is no session with id [2e9e317f-7575-4bb0-98c4-3e6e5d2578f5]
ehcache-shiro.xml添加session缓存属性
<!-- session缓存 -->
<cache name="shiro-activeSessionCache"
       maxEntriesLocalHeap="2000"
       eternal="false"
       timeToIdleSeconds="0"
       timeToLiveSeconds="0"
       overflowToDisk="false"
       statistics="true">
</cache>

注意: 这里一定要注意缓存的设置过期时间,还有setGlobalSessionTimeout 的值,任一个时间设置的比较短,session就会从ehcache中清除,到时候就会报
There is no session with id [2e9e317f-7575-4bb0-98c4-3e6e5d2578f5]

shiro取消url上面的JSESSIONID
//取消url 后面的 JSESSIONID
sessionManager.setSessionIdUrlRewritingEnabled(false);
完整的sessionManager如下:
/**
 * 配置会话管理器,设定会话超时及保存
 * @return
 */
@Bean("sessionManager")
public SessionManager sessionManager() {

    DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
    Collection<SessionListener> listeners = new ArrayList<SessionListener>();
    //配置监听
    listeners.add(sessionListener());
    sessionManager.setSessionListeners(listeners);
    sessionManager.setSessionIdCookie(sessionIdCookie());
    sessionManager.setSessionDAO(sessionDAO());
    sessionManager.setCacheManager(ehCacheManager());

    //全局会话超时时间(单位毫秒),默认30分钟  暂时设置为10秒钟 用来测试
    sessionManager.setGlobalSessionTimeout(10000);
    //是否开启删除无效的session对象  默认为true
    sessionManager.setDeleteInvalidSessions(true);
    //是否开启定时调度器进行检测过期session 默认为true
    sessionManager.setSessionValidationSchedulerEnabled(true);
    //设置session失效的扫描时间, 清理用户直接关闭浏览器造成的孤立会话 默认为 1个小时
    //设置该属性 就不需要设置 ExecutorServiceSessionValidationScheduler 底层也是默认自动调用ExecutorServiceSessionValidationScheduler
    //暂时设置为 5秒 用来测试
    sessionManager.setSessionValidationInterval(5000);

    //取消url 后面的 JSESSIONID
    sessionManager.setSessionIdUrlRewritingEnabled(false);

    return sessionManager;

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在使用 Spring Boot 整合 Shiro 进行开发时,可以通过配置 Shirosession 过期时间来实现会话超时。具体实现步骤如下: 1. 在 Shiro 的配置文件中配置 session 过期时间: ```xml <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager"> <!-- session 过期时间,单位毫秒 --> <property name="globalSessionTimeout" value="1800000"/> <!-- 是否开启删除无效的 session 对象 --> <property name="deleteInvalidSessions" value="true"/> </bean> ``` 2. 在 Spring Boot 的配置类中配置 Shiro 过滤器: ```java @Configuration public class ShiroConfig { @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); // 必须设置 SecurityManager shiroFilterFactoryBean.setSecurityManager(securityManager); // 配置登录的 url 和登录成功的 url shiroFilterFactoryBean.setLoginUrl("/login"); shiroFilterFactoryBean.setSuccessUrl("/index"); // 配置未授权跳转页面 shiroFilterFactoryBean.setUnauthorizedUrl("/403"); // 配置访问权限 LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); // 过滤链定义,从上向下顺序执行,一般将 /** 放在最为下边 // authc:所有 url 都必须认证通过才可以访问;anon:所有 url 都都可以匿名访问 filterChainDefinitionMap.put("/login", "anon"); filterChainDefinitionMap.put("/logout", "logout"); filterChainDefinitionMap.put("/static/**", "anon"); filterChainDefinitionMap.put("/js/**", "anon"); filterChainDefinitionMap.put("/css/**", "anon"); filterChainDefinitionMap.put("/images/**", "anon"); filterChainDefinitionMap.put("/**", "authc"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } @Bean public DefaultWebSecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(userRealm()); securityManager.setSessionManager(sessionManager()); return securityManager; } @Bean public UserRealm userRealm() { return new UserRealm(); } @Bean public DefaultWebSessionManager sessionManager() { DefaultWebSessionManager sessionManager = new DefaultWebSessionManager(); // 设置 session 过期时间,单位毫秒 sessionManager.setGlobalSessionTimeout(1800000); // 删除无效的 session 对象 sessionManager.setDeleteInvalidSessions(true); return sessionManager; } } ``` 通过以上配置,可以在 Shiro 中实现 session 会话超时功能。在用户登录后,session 的过期时间会被设置为配置的时间,当用户在该时间内没有操作时,session 会话将被 Shiro 销毁,用户需要重新登录。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值