Spring Security(九):会话管理(Session)

Spring Security可以与Spring Session库配合使用,只需要做一些简单的配置就可以实现一些功能,如(会话过期、一个账号只能同时在线一个、集群session等)

一:会话超时

1. application.yml

配置session会话超时时间,默认为30秒,但是Spring Boot中的会话超时时间至少为60秒

server:
  servlet:
    session:
      timeout: 60

2. security configuration

配置session超时后地址

http.csrf().disable()
	.antMatchers("/login", "/session/invalid").permitAll()
	.sessionManagement()
          .invalidSessionUrl("/session/invalid")

3. SessionController

@RestController
public class SessionController {

    @GetMapping("/session/invalid")
    @ResponseStatus(code = HttpStatus.UNAUTHORIZED)
    public String sessionInvalid() {
        return "session失效";
    }
}
@RestController
@RequestMapping("/user")
public class UserController {
    @GetMapping("/me")
    public String me() {
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        return JSON.toJSONString(auth);
    }
}

4. 测试

  1. 先登录/login,随意访问一个地址如"/user/me"获取用户的信息
  2. 等待一分钟,然后重新访问/user/me,结果是跳转到"/session/invalid"地址上了

二:同一个账号同时在线个数

同一个账号同时在线个数如果设置为1表示,该账号在同一时间内只能有一个有效的登录,如果同一个账号又在其它地方登录,那么就将上次登录的会话过期,即后面的登录会踢掉前面的登录

为了演示该功能我们调整会话超时未1个小时,以便有足够的时间来操作。

1. application.yml

server:
  servlet:
    session:
      timeout: 600

2. security configuration

  • maximumSessions:最大会话数量,设置为1表示一个用户只能有一个会话
  • expiredSessionStrategy:会话过期策略
http.csrf().disable()
	.antMatchers("/login", "/session/invalid").permitAll()
	.sessionManagement()
          .invalidSessionUrl("/session/invalid")
          .maximumSessions(1)
          .expiredSessionStrategy(new MyExpiredSessionStrategy())
public class MyExpiredSessionStrategy implements SessionInformationExpiredStrategy {

    @Override
    public void onExpiredSessionDetected(SessionInformationExpiredEvent event) throws IOException, ServletException {
        HttpServletResponse response = event.getResponse();
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write("并发登录");
    }
}

3. 测试

  1. 使用chrome浏览器,先登录,再访问/user/me
  2. 使用Firefox浏览器,再登录,再访问/user/me
  3. 使用chrome浏览器,重新访问/user/me,会执行expiredSessionStrategy,页面上显示”并发登录“

4. 阻止用户第二次登录

sessionManagement也可以配置 maxSessionsPreventsLogin:boolean值,当达到maximumSessions设置的最大会话个数时阻止登录。

http.csrf().disable()
	.antMatchers("/login", "/session/invalid").permitAll()
	.sessionManagement()
          .invalidSessionUrl("/session/invalid")
          .maximumSessions(1)
          .expiredSessionStrategy(new MyExpiredSessionStrategy())
          .maxSessionsPreventsLogin(true)
          .and().and()     
	.logout().permitAll();

三:集群session

在这里插入图片描述
实际场景中一个服务会至少有两台服务器在提供服务,在服务器前面会有一个nginx做负载均衡,用户访问nginx,nginx再决定去访问哪一台服务器。当一台服务宕机了之后,另一台服务器也可以继续提供服务,保证服务不中断。如果我们将session保存在Web容器(比如tomcat)中,如果一个用户第一次访问被分配到服务器1上面需要登录,当某些访问突然被分配到服务器二上,因为服务器二上没有用户在服务器一上登录的会话session信息,服务器二还会再次让用户登录,用户已经登录了还让登录就感觉不正常了。解决这个问题的思路是用户登录的会话信息不能再保存到Web服务器中,而是保存到一个单独的库(redis、mongodb、jdbc等)中,所有服务器都访问同一个库,都从同一个库来获取用户的session信息,如用户在服务器一上登录,将会话信息保存到库中,用户的下次请求被分配到服务器二,服务器二从库中检查session是否已经存在,如果存在就不用再登录了,可以直接访问服务了。

1. 引入spring session依赖

<dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-data-redis</artifactId>
    <version>2.1.5.RELEASE</version>
</dependency>
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.9.0</version>
</dependency>

2. application.properties

spring:
  session:
    store-type: redis
  redis:
    host: localhost
    port: 6379

server:
  port: 8080
  servlet:
    session:
      timeout: 600

3. 启动两个应用

用两个不同的端口分别启动应用

mvn clean install
java -jar spring-security-example-0.0.1-SNAPSHOT.jar --server.port=8080
java -jar spring-security-example-0.0.1-SNAPSHOT.jar --server.port=8081

4. 测试

  1. 使用其中一个服务去登录 http://localhost:8080/login
  2. 使用另一个服务访问任意接口 http://localhost:8081/user/me 不需要再重新登录就可以直接访问

注意:session是有过期时间的,当session过期了,redis中的key也就会被自动删除。
在这里插入图片描述

发布了308 篇原创文章 · 获赞 936 · 访问量 133万+

猜你喜欢

转载自blog.csdn.net/vbirdbest/article/details/90550702