我在限制每个用户的最大并发会话数方面遇到问题。我尝试了在这里和其他网站上找到的一些解决方案,但仍然没有成功。我最终得到了这样的结果:
登录控制器
@RestController
public class LoginController {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private SecurityContextRepository contextRepository;
private SecurityContextHolderStrategy contextHolderStrategy = SecurityContextHolder.getContextHolderStrategy();
@PostMapping(value = "/auth/signIn", produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<String> signIn(@RequestBody LoginForm loginForm, HttpServletRequest request, HttpServletResponse response) {
Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(loginForm.getUsername(), loginForm.getPassword()));
SecurityContext context = contextHolderStrategy.createEmptyContext();
context.setAuthentication(authentication);
contextHolderStrategy.setContext(context);
contextRepository.saveContext(context, request, response);
return new ResponseEntity<>("test", HttpStatus.OK);
}
会话配置
@Configuration
@EnableRedisHttpSession
public class SessionConfig {
@Bean
public LettuceConnectionFactory redisConnectionFactory(){
return new LettuceConnectionFactory();
}
@Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
return new HttpSessionEventPublisher();
}
@Bean
public RedisOperations<String, Object> sessionRedisOperations(){
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory());
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
return redisTemplate;
}
@Bean
public FindByIndexNameSessionRepository redisSessionRepository(RedisOperations<String, Object> sessionRedisOperations) {
return new RedisIndexedSessionRepository(sessionRedisOperations);
}
public class SessionInitializer extends AbstractHttpSessionApplicationInitializer {
public SessionInitializer(){
super(SessionConfig.class);
}
}
public class SecurityWebInitializer extends AbstractSecurityWebApplicationInitializer {
@Override
protected boolean enableHttpSessionEventPublisher() {
return true;
}
}
}
安全配置
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Autowired
private FindByIndexNameSessionRepository sessionRepository;
@Bean
public SpringSessionBackedSessionRegistry sessionRegistry() {
return new SpringSessionBackedSessionRegistry<>(this.sessionRepository);
}
@Bean
public SecurityContextRepository securityContextRepository(){
return new HttpSessionSecurityContextRepository();
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable().authorizeHttpRequests()
.requestMatchers("/auth/**").permitAll()
.anyRequest().authenticated().and()
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
.sessionManagement()
.maximumSessions(1)
.maxSessionsPreventsLogin(true)
.expiredUrl("/expired")
.sessionRegistry(sessionRegistry());
return http.build();
}
}
我还在我的 UserDetail 实现中实现了 hashcode 和 equals 方法,因为我发现这也可能导致问题。 当我使用 Postman 登录时,删除会话 cookie 并再次登录,Spring 继续在 Redis 中创建另一个会话。你能发现我做错了什么吗?