会话管理

简单总结下springsecurity 对会话的管理配置

配置

配置 http.authorizeRequests().sessionManagement() 做配置的相关操作

1
2
.and().sessionManagement();

会话禁用

1
.sessionManagement().disable();

会话过期配置

  • 配置过期跳转url

    1
    .invalidSessionUrl("/xx")
  • 自定义过期策略

    1
    2
    3
    4
    5
    6
    invalidSessionStrategy(new InvalidSessionStrategy() {
    @Override
    public void onInvalidSessionDetected(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {

    }
    })

会话并发控制

实现用户只能有在一个设备上的登录的功能;

1
2
.sessionManagement().maximumSessions(1);

默认是踢掉前一个登录的人

具体实现逻辑可查看 ConcurrentSessionControlAuthenticationStrategy#onAuthentication

如果要实现后一个不能登录需要这样配置

maxSessionsPreventsLogin 禁止新用户登录

1
2
.maximumSessions(1).maxSessionsPreventsLogin(true)

实现说明

对于默认情况下的会话信息通过类 SessionRegistryImpl 进行session的存储

通过2个map维护session的相关信息;

需要注意的一点是: principals 的map的key 是 principal 也就是userdetails 对象,
在操作map的时候 使用的 也就是userdetails 是自定义的 并且未针对此场景重写hashcode 和equals 方法,那么就无法做到并发控制

因为根据当前用户信息查询所有的session的时候根据查不到,需要对userdetial做下实现能够根据用户相关的信息定义hashcode和equals

结论:自定义的userdetail 要以username 信息和重写hashcode 和equals ,因为要实现对同一个用户名的并发控制管理.

会话外部存储

注意:以上的配置在单机环境下是可以生效的,但是在集群多机器下就不一定了。因为默认的会话信息的保存是保存在jvm内存中的。

使用redis存储会话信息

需要添加spring session redis相关依赖;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-core</artifactId>
<version>2.4.0</version>
</dependency>

<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
<version>2.4.0</version>
</dependency>

添加redis实现的SessionRegistry

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@EnableRedisHttpSession
public class SessionConfig {


@Autowired
private FindByIndexNameSessionRepository findByIndexNameSessionRepository;


@Bean
public SessionRegistry springSessionBackedSessionRegistry(){
return new SpringSessionBackedSessionRegistry(findByIndexNameSessionRepository);
}

}


配置到security中

1
2

.sessionManagement().maximumSessions(1).maxSessionsPreventsLogin(true).sessionRegistry(springSessionBackedSessionRegistry);

执行等后就可以看到会话信息存储到了redis中。即使服务重启会话也没有过期;