在之前的几章里面,我们分别做了快速入门、自定义表单登录、自定义手机登录。他们有一个共同点,就是目前我们与客户端之间的交互都依赖于 Session。那么本章我们就带大家来了解一下 SpringSecurity 下 Session 的使用与设置。
Session 是什么?
Session 中文意思为会议,在计算机中,尤其在网络应用中称为会话控制。
Session直接翻译成中文比较困难,一般都译成时域。在计算机专业术语中,Session是指一个终端用户与交互系统进行通信的时间间隔,通常指从注册进入系统到注销退出系统之间所经过的时间。以及如果需要的话,可能还有一定的操作空间。
百度百科
为什么要使用 Session?
Http 协议本身是无状态的,多次连接之间的状态无法共享,服务端就判断是否是同一个用户操作的就比较麻烦。
而 Session 就是用来保存状态的,允许通过将对象存储在 Web 服务器内存中,在整个用户会话过程中进行共享。
Session 原理
浏览器第一次访问服务器时,服务端会生成 Session,然后同时为该 Session 生成一个唯一的 SessionId,然后将 SessionId 与 Session 以键值对的形式存储到内存中(Java 默认)。将 SessionId 以 Cookie 的方式返回给浏览器。浏览器下一次访问的时候就携带 SessionId 来访问,这样服务端通过查询就可以取出 Session 达到共享了。
在 SpringSecurity 里面的使用
单机 Session
-
Session 超时设置
-
设置 Session 超时时间,在系统配置文件(application.properties/yml) 中配置
server: session: timeout: 600 #单位为 Second,设置的时间低于 1 分钟,按 1 分钟处理
-
设置 Session 超时/失效后继续访问处理,可以设置跳转页面,也可以设置跳转接口。前后端分离一般返回 Json,不分离可以引导页面跳转。两者都要兼容,可以跳转到自定义接口判断实际请求,进行处理。
// 设置 Session 失效后访问跳转的页面 http.sessionManagement().invalidSessionUrl("/session/invalid");
-
-
Session 并发控制
-
Session 最大数设置,限制系统中用户数量
http.sessionManagement().maximumSessions(1)// 设置同时在线的最大 Session 数
-
设置 Session 数量达到最大值时,是否阻止后面用户登录
// 设置当 Session 数量达到最大数量时,阻止后面的登录 http.sessionManagement().maxSessionsPreventsLogin(true);
-
自定义设置 Session 数量达到最大时后续登录的处理策略,默认实现会将前面用户的 Session 置为失效
// 设置 Session 并发登录时处理策略 http.sessionManagement().expiredSessionStrategy(new DefaultExpiredSessionStrategy());
/** * @author: hblolj * @Date: 2019/3/16 9:13 * @Description: Session 并发登录时的处理策略 * @Version: **/ public class DefaultExpiredSessionStrategy implements SessionInformationExpiredStrategy{ @Override public void onExpiredSessionDetected(SessionInformationExpiredEvent event) throws IOException, ServletException { event.getResponse().setCharacterEncoding("UTF-8"); event.getResponse().getWriter().write("并发登录!"); } }
-
集群 Session(单机 Session 下的设置在集群下一样有效)
在单机 Session 下基本上只要处理好上面的问题就可以了,但是随着系统越来越大,单体应用可能满足不了我们的需求。如果我们对应用进行了集群,那么用户在每个应用上都会产生一个 Session,并且这些 Session 还不是共享的,就达不到 Session 使用的初衷了。
为了让 Session 可以共享,一般我们就会改变 Session 的存储方式,Session 默认存储在内存中,多个主机间就不方便共享。主流处理方案有:
- 保存在数据库
- 保存在 Redis 中
- 利用 Servlet 容器来进行 Session 同步
- 或者使用 Nginx 使用用户 ip 进行哈希处理,在负载均衡时,让用户每次访问同一个主机避免多 Session 问题
我们这里主要还是讨论 SpringSecurity 下集群 Session 的处理方案,其他扩展信息,大家感兴趣可以自行查询,或者留言讨论。
我们这里示例使用 Redis 作为 Session 存储方式,通过下列配置 Session 就会默认存储在 Redis 中了。关于 SpringBoot 下 Redis 的使用,不了解的可以查询资料使用,上手还是比较简单的。
-
使用 Redis 作为 Session 的存储,通过配置文件指定存储方式。
spring: session: store-type: redis #设置将 Session 存储在 Redis 中 redis: host: redis 所在的主机 port: 默认是 6379 database: 选择使用的 database password: your self password pool: max-wait: -1 max-idle: 1 max-active: 8 min-idle: 0
-
添加 Redis 依赖
<!-- SpringBoot 版本 1.5.19.RELEASE--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
-
添加 Spring-Session 依赖
<!-- SpringBoot 版本 1.5.19.RELEASE--> <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session</artifactId> </dependency>
-
除了 Redis,SpringBoot 还支持一下存储方式,大家可以自行扩展使用
public enum StoreType { REDIS, MONGO, JDBC, HAZELCAST, HASH_MAP, NONE; private StoreType() { } }
退出登录
既然 Session 中共享用户多次请求的状态,那么如果我们要用户登出,就只要清空 Session 即可。那么在 SpringSecurity 下是怎样设置的呢?
-
如何退出
-
请求登出地址,默认为 /logout
-
自定义登出地址
http.logout().logoutUrl("/quit");// 默认是 logout
-
-
退出处理原理 (访问退出地址,退出处理逻辑 SpringSecurity 有默认实现)
-
使当前 Session 失效
(HttpSession)session.invalidate();
-
清除与当前用户先关的 remember-me 记录
if (authentication != null) { this.tokenRepository.removeUserTokens(authentication.getName()); }
-
清空当前的 SecurityContext
if (this.clearAuthentication) { SecurityContext context = SecurityContextHolder.getContext(); context.setAuthentication((Authentication)null); } SecurityContextHolder.clearContext();
-
-
与退出相关的配置
-
自定义退出成功跳转页面或处理策略,同时设置只有 logoutSuccessHandler 会生效
http .logout() .logoutSuccessUrl("http://www.baidu.com") // 设置退出成功跳转的页面 .logoutSuccessHandler(new DefaultLogoutSuccessHandler()) // 设置退出成功的处理策略
/** * @author: hblolj * @Date: 2019/3/16 10:28 * @Description: * @Version: **/ @Slf4j public class DefaultLogoutSuccessHandler implements LogoutSuccessHandler{ @Override public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException { log.info("Logout Success!"); } }
-
设置退出时删除客户端 Cookie
http.logout().deleteCookies("JSESSIONID") // 退出时删除客户端指定的 Cookie
-
OK,到了这里,基本上在 SpringSecurity 里面 Session 的常规设置与使用都介绍完了。大家如果有什么问题,欢迎在下面的留言区留言讨论。
实际上,在现在的开发中,单体 Web 应用可能还是以 Session 为主,但是如果要同时支持 App 的话, Session 就不是那么好用了,因为 Session 依赖 Cookie,虽然 App 访问时可以模拟浏览器设置 Cookie,但是契合度还是不如 Web,所以对于 App 或者集群环境下,现在主流使用基于 OAuth2 的 Token 作为验证。是不是以为我们下一章要讲 SpringSecurity 下 OAuth2 协议的集成与 Token 使用呢?哈哈,下一章我们计划带大家集成一下 SpringSecurity 下第三方登录,比如 QQ 快速登录、微信快速登录等(也依赖于 OAuth 协议)。至于集成 Token 认证还要往后几章哦!
To Be Continue!