例如:密码登录和短信验证码登录都是提交表单完成的,社交账号登录是基于OAuth2协议完成的。这三种登录方式共同点就是:
登录成功之后用户信息是放在服务器session中的。
这几讲讲常见的session管理:
1.session超时处理:(用户登录之后多长时间不操作,session超时做哪些处理)
设置server.session.timeout = 10 #单位秒
不生效
原因是:
然而并没有什么用,因为SpringBoot在TomcatServletWebServerFactory代码中写了这个
1 private long getSessionTimeoutInMinutes() {
2 Duration sessionTimeout = this.getSession().getTimeout();
3 return this.isZeroOrLess(sessionTimeout) ? 0L : Math.max(sessionTimeout.toMinutes(), 1L);
4 }
把设置的时间转化成分钟和1分钟取最大值。
在springboot2.0之后,设置session超时的方式修改为在application.yml或application.xml上面添加
image.pngserver.servlet.session.timeout=PT1M
PT1M 意思是设置session失效的时间是1分钟。
扩展:Duration
通过查看springboot源码发现setTimeouot方法,这里要求传入Duration的实例
public void setTimeout(Duration timeout) {
this.timeout = timeout;
}
而Duration是在Java8中新增的,主要用来计算日期差值,Duration是被final声明的,并且是线程安全的
转换字符串方式,类似于 SimpleDateFormat 的格式化日期方式
Duration 字符串类似数字有正负之分,默认正,负以’-‘开头,紧接着’P’,下面所有字母都不区分大小写:
‘D’ – 天
‘H’ – 小时
‘M’ – 分钟
‘S’ – 秒
字符’T’是紧跟在时分秒之前的,每个单位都必须由数字开始,且时分秒顺序不能乱,比如:P2DT3M5S,P3D,PT3S
PT3M2S 等于 -PT-3M-2S
最后总结一下Duration最实用的一个功能其实是 between 方法,因为有很多时候我们需要计算两个日期之间的天数或者小时数,用这个就可以很方便的进行操作。
为了便于显示由于session过期引起的需要配置:
.and()
.sessionManagement().invalidSessionUrl("session/invalid") //session过期后跳转的URL
.and()
/**
* 配置session失效后跳转的url
* @return
*/
@GetMapping("/session/invalid")
@ResponseStatus(code = HttpStatus.UNAUTHORIZED)
public SimpleResponse sessionInvalid(){
String message = "session失效";
return new SimpleResponse(message);
}
2.session并发控制:(用户在A机器上登陆后,在B机器又登录,需要把A机器的session干掉)
.maximumSessions(1)//设置session最大并发数量
.maxSessionsPreventsLogin(true)//设置当session到达最大数量阻止后续的登录而不是顶掉前一个
.expiredSessionStrategy(new YunqingExpiredSessionStrategy())//设置被顶掉之后的处理
/**
- session被别人登录顶掉之后的处理
*/
public class YunqingExpiredSessionStrategy implements SessionInformationExpiredStrategy {
@Override
public void onExpiredSessionDetected(SessionInformationExpiredEvent event) throws IOException, ServletException {
event.getResponse().setContentType("application/json;charset=UTF-8");
event.getResponse().getWriter().write("并发登陆");
}
}
3.集群session管理:(默认session是都放在中间件服务器里的,比如Tomcat,例如负载均衡没做session粘连处理,A机器的登录session不会被B机器知道,请求发到B机器上后还要求登陆)
image.png
解决方案如上图,把session抽取出来放到一个独立的储存里,这样哪个服务器都需要到独立的存储里去拿session.
spring-session帮助我们完成了此功能。
下图中所示,spring session支持的独立存储session的方式。
image.png
spring-session存储类型,解决session集群问题,剥离出session独立存储在redis
spring.session.store-type = redis
这里讲一下redis存储session,因为session是一个频繁访问的,是有超时时间的,要是存在数据库还要去清理,redis本身就可以设置超时时间。所以使用redis. 因为使用spring-session管理之后,session放到redis,所以验证码的实体类需要序列化。但是验证码的图片怎么序列化?如下解决:
/**
* 保存校验码
*
* @param request
* @param validateCode
/
private void save(ServletWebRequest request, T validateCode) {
/*
* 因为图片BufferedImage类型不能序列化,所以这里取到ValidateCode不管是有没有图片,都重新new一个实例,
* 只取码和过期时间保存到session中
*/
ValidateCode code = new ValidateCode(validateCode.getCode(), validateCode.getExpireTime());
validateCodeRepository.save(request, code, getValidateCodeType(request));
}
注意这里有一个大坑:
因为我用的springboot2.1.6版本,所以使用spring-session依赖实现session共享的时候出现了很多莫名其妙的问题,后来换成了spring-session-data-redis依赖就解决了。。。。
4.退出
spring security默认的退出处理<a href="/logout">退出</a>逻辑:
1.使当前的session失效
2.清除与当前用户相关的rememberMe-me记录
3.清空当前的SecurityContext
4.重定向到登录页
网友评论