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 并发的配置就生效了,在集群环境下,用户也只可以在一台设备上登录