http.sessionManagement()
当用户的session会话失败(请求携带着无效的JSESSIONID访问系统)时,可以制定相关策略对会话失效的请求进行处理
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
/**
* 定制基于 HTTP 请求的用户访问控制
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
.and()
.authorizeRequests()
.anyRequest()
.authenticated();
// 开启 Session 会话管理配置
http.sessionManagement()
// 设置 Session 会话失效时重定向路径,默认为 loginPage()
.invalidSessionUrl("/login/page");
}
}
# session 失效时间,单位是秒,默认为 30min
server.servlet.session.timeout=30m
# JSESSIONID (Cookie)的生命周期,单位是秒,默认为 -1
server.servlet.session.cookie.max-age=-1
注意:Session 的失效时间至少要 1 分钟,少于 1 分钟按照 1 分钟配置
/**
* 用户请求携带无效的 JSESSIONID 访问时的处理策略,即对应的 Session 会话失效
*/
@Component
public class CustomInvalidSessionStrategy implements InvalidSessionStrategy {
private final RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
@Override
public void onInvalidSessionDetected(HttpServletRequest request, HttpServletResponse response) throws IOException {
// 清除浏览器中的无效的 JSESSIONID
Cookie cookie = new Cookie("JSESSIONID", null);
cookie.setPath(getCookiePath(request));
cookie.setMaxAge(0);
response.addCookie(cookie);
String xRequestedWith = request.getHeader("x-requested-with");
// 判断前端的请求是否为 ajax 请求
if ("XMLHttpRequest".equals(xRequestedWith)) {
// 响应 JSON 数据
response.setContentType("application/json;charset=utf-8");
response.getWriter().write("SESSION 失效,请重新登录!");
}else {
// 重定向到登录页面
redirectStrategy.sendRedirect(request, response, "/login/page");
}
}
private String getCookiePath(HttpServletRequest request) {
String contextPath = request.getContextPath();
return contextPath.length() > 0 ? contextPath : "/";
}
}
@Configuration
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
// 自定义 Session 会话失效策略
@Autowired
private CustomInvalidSessionStrategy invalidSessionStrategy;
/**
* 定制基于 HTTP 请求的用户访问控制
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
.and()
.authorizeRequests()
.anyRequest()
.authenticated();
// 开启 Session 会话管理配置
http.sessionManagement()
// 设置 Session 会话失效时重定向路径,默认为 loginPage()
// .invalidSessionUrl("/login/page")
// 配置使用自定义的 Session 会话失效处理策略
.invalidSessionStrategy(invalidSessionStrategy);
}
}
如果同一个用户在第二个地方登录,则不允许他二次登录
@Configuration
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
/**
* 定制基于 HTTP 请求的用户访问控制
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
.and()
.authorizeRequests()
.anyRequest()
.authenticated();
// 开启 Session 会话管理配置
http.sessionManagement()
// 配置使用自定义的 Session 会话失效处理策略
.invalidSessionStrategy(invalidSessionStrategy)
// 设置单用户的 Session 最大并发会话数量,-1 表示不受限制
.maximumSessions(1)
// 设置为 true,表示某用户达到最大会话并发数后,新会话请求会被拒绝登录
.maxSessionsPreventsLogin(true);
// 设置所要使用的 sessionRegistry,默认为 SessionRegistryImpl 实现类
.sessionRegistry(sessionRegistry());
}
/**
* 注册 SessionRegistry,该 Bean 用于管理 Session 会话并发控制
*/
@Bean
public SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
/**
* 配置 Session 的监听器(注意:如果使用并发 Sessoion 控制,一般都需要配置该监听器)
* 解决 Session 失效后, SessionRegistry 中 SessionInformation 没有同步失效的问题
*/
@Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
return new HttpSessionEventPublisher();
}
}
如果同一个用户在第二个地方登录,则将第一个踢下线
/**
* 前提:Session 并发处理的配置为 maxSessionsPreventsLogin(false)
* 用户的并发 Session 会话数量达到上限,新会话登录后,最老会话会在下一次请求中失效,并执行此策略
*/
@Component
public class CustomSessionInformationExpiredStrategy implements SessionInformationExpiredStrategy {
private final RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
@Override
public void onExpiredSessionDetected(SessionInformationExpiredEvent event) throws IOException {
HttpServletRequest request = event.getRequest();
HttpServletResponse response = event.getResponse();
// 最老会话被踢下线时显示的信息
UserDetails userDetails = (UserDetails) event.getSessionInformation().getPrincipal();
String msg = String.format("用户[%s]在另外一台机器登录,您已下线!", userDetails.getUsername());
String xRequestedWith = event.getRequest().getHeader("x-requested-with");
// 判断前端的请求是否为 ajax 请求
if ("XMLHttpRequest".equals(xRequestedWith)) {
// 认证成功,响应 JSON 数据
response.setContentType("application/json;charset=utf-8");
response.getWriter().write(msg);
}else {
// 返回到登录页面显示信息
AuthenticationException e = new AuthenticationServiceException(msg);
request.getSession().setAttribute("SPRING_SECURITY_LAST_EXCEPTION", e);
redirectStrategy.sendRedirect(request, response, "/login/page?error");
}
}
}
@Configuration
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private CustomSessionInformationExpiredStrategy sessionInformationExpiredStrategy;// 自定义最老会话失效策略
/**
* 定制基于 HTTP 请求的用户访问控制
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
// 开启 Session 会话管理配置
http.sessionManagement()
// 配置使用自定义的 Session 会话失效处理策略
.invalidSessionStrategy(invalidSessionStrategy)
// 设置单用户的 Session 最大并发会话数量,-1 表示不受限制
.maximumSessions(1)
// 设置为 true,表示某用户达到最大会话并发数后,新会话请求会被拒绝登录;
// 设置为 false,表示某用户达到最大会话并发数后,新会话请求访问时,其最老会话会在下一次请求时失效
.maxSessionsPreventsLogin(false)
// 设置所要使用的 sessionRegistry,默认为 SessionRegistryImpl 实现类
.sessionRegistry(sessionRegistry())
// 最老会话在下一次请求时失效,并重定向到 /login/page
//.expiredUrl("/login/page");
// 最老会话在下一次请求时失效,并按照自定义策略处理
.expiredSessionStrategy(sessionInformationExpiredStrategy);
}
/**
* 注册 SessionRegistry,该 Bean 用于管理 Session 会话并发控制
*/
@Bean
public SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
/**
* 配置 Session 的监听器(如果使用并发 Sessoion 控制,一般都需要配置)
* 解决 Session 失效后, SessionRegistry 中 SessionInformation 没有同步失效问题
*/
@Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
return new HttpSessionEventPublisher();
}
}
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
<dependency>
<groupId>org.apache.commonsgroupId>
<artifactId>commons-pool2artifactId>
<version>2.8.0version>
dependency>
<dependency>
<groupId>org.springframework.sessiongroupId>
<artifactId>spring-session-data-redisartifactId>
dependency>
# Redis 服务器地址
spring.redis.host=localhost
# Redis 服务器连接端口
spring.redis.port=6379
# Redis 服务器连接密码(默认无)
spring.redis.password=
# Redis数据库索引(默认为0)
spring.redis.database=1
# 连接池最大连接数(使用负值表示没有限制),默认 8
spring.redis.lettuce.pool.max-active=100
# 连接池大阻塞等待时间(使用负值表示没有限制),默认 -1
spring.redis.lettuce.pool.max-wait=PT10S
# 连接池中的大空闲连接 默认 8
spring.redis.lettuce.pool.max-idle=10
# 连接池中的小空闲连接 默认 0
spring.redis.lettuce.pool.min-idle=1
# 连接超时时间
spring.redis.timeout=PT10S
# 使用 Redis 存储 Session,默认为 none(使用内存存储)
spring.session.store-type=redis
# 指定存储 SessionId 的 Cookie 名(使用 Redis 存储 Session 后,Cookie 名默认会变为 SESSION)
server.servlet.session.cookie.name=JSESSIONID
Redis 存储 Session 默认的序列化方式为 JdkSerializationRedisSerializer,所以存入 Session 的对象都要实现 Serializable 接口