HttpFirewall
在 Spring Security
中提供了一个 HttpFirewall
,看名字就知道这是一个请求防火墙,它可以自动处理掉一些非法请求。
HttpFirewall
目前一共有两个实现类:
一个是严格模式的防火墙设置,还有一个默认防火墙设置。
DefaultHttpFirewall
的限制相对于StrictHttpFirewall
要宽松一些,当然也意味着安全性不如StrictHttpFirewall。
Spring Security 中默认使用的是 StrictHttpFirewall。
首先 创建一个 Spring Boot 工程,引入
Web、Spring Session、Spring Security 以及 Redis
:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
spring.redis.password=123
spring.redis.port=6379
spring.redis.host=127.0.0.1
spring.security.user.name=javaboy
spring.security.user.password=123
server.port=8080
; 配置一下 Redis
配置完成后 ,就可以使用 Spring Session 了,其实就是使用普通的 HttpSession ,其他的 Session 同步到 Redis 等操作,框架已经自动帮你完成了:
@RestController
public class HelloController {
@Value("${server.port}")
Integer port;
@GetMapping("/set")
public String set(HttpSession session) {
session.setAttribute("user", "abccd");
return String.valueOf(port);
}
@GetMapping("/get")
public String get(HttpSession session) {
return session.getAttribute("user") + ":" + port;
}
}
- Spring Boot 将以集群的方式启动 ,为了获取每一个请求到底是哪一个 Spring Boot 提供的服务,需要在每次请求时返回当前服务的端口号,因此这里我注入了 server.port 。
Security 配置
- 解决会话抢占问题(单节点通过 session 缓存实现)
- 会话注册表的维护默认是由
SessionRegistryImpl
来维护的,而SessionRegistryImpl
的维护就是基于内存的维护。 - 现在我们虽然启用了 Spring Session+Redis 做 Session 共享,但是
SessionRegistryImpl
依然是基于内存来维护的,所以我们要修改SessionRegistryImpl
的实现逻辑。
- 修改方式也很简单,实际上 Spring Session 为我们提供了对应的实现类
SpringSessionBackedSessionRegistry
,具体配置如下:
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
FindByIndexNameSessionRepository sessionRepository;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest()
...
.sessionManagement()
.maximumSessions(1)
.maxSessionsPreventsLogin(true)
.sessionRegistry(sessionRegistry());
}
@Bean
SpringSessionBackedSessionRegistry sessionRegistry() {
return new SpringSessionBackedSessionRegistry(sessionRepository);
}
}
- 我们在这里只需要提供一个
SpringSessionBackedSessionRegistry
的实例,并且将其配置到sessionManagement
中去即可。 - 以后,session 并发数据的维护将由
SpringSessionBackedSessionRegistry
来完成,而不是SessionRegistryImpl,如此,我们关于
session 并发的配置就生效了,在集群环境下,用户也只可以在一台设备上登录。