Spring Security Session管理(6)

导读:本篇文章讲解 Spring Security Session管理(6),希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

用户登录成功后,信息保存在服务器Session中,这节学习下如何管理这些Session。这节将在Spring Security短信验证码登录的基础上继续扩展。

1. Session超时设置

Session超时时间也就是用户登录的有效时间。要设置Session超时时间很简单,只需要在配置文件中添加:

server:
  servlet:
    session:
      timeout: 60

单位为秒,通过上面的配置,Session的有效期为一个小时。

值得注意的是,Session的最小有效期为60秒,也就是说即使你设置为小于60秒的值,其有效期还是为60秒。查看TomcatEmbeddedServletContainerFactory的源码即可发现原因:

image-20220425194145569

Session失效后,刷新页面后将跳转到认证页面,我们可以再添加一些配置,自定义Session失效后的一些行为。

在Spring Security中配置Session管理器,并配置Session失效后要跳转的URL:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class) // 添加验证码校验过滤器
        .addFilterBefore(smsCodeFilter,UsernamePasswordAuthenticationFilter.class) // 添加短信验证码校验过滤器
            .formLogin() // 表单登录
                .loginPage("/authentication/require") // 登录跳转 URL
                .loginProcessingUrl("/login") // 处理表单登录 URL
                .successHandler(authenticationSucessHandler) // 处理登录成功
                .failureHandler(authenticationFailureHandler) // 处理登录失败
            .and()
                .authorizeRequests() // 授权配置
                .antMatchers("/authentication/require",
                        "/login.html", "/code/image","/code/sms","/session/invalid").permitAll() // 无需认证的请求路径
                .anyRequest()  // 所有请求
                .authenticated() // 都需要认证
            .and()
                .sessionManagement() // 添加 Session管理器
                .invalidSessionUrl("/session/invalid") // Session失效后跳转到这个链接
            ......
}

上面配置了Session失效后跳转到/session/invalid,并且将这个URL添加到了免认证路径中。

在Controller里添加一个方法,映射该请求:

@GetMapping("session/invalid")
@ResponseStatus(HttpStatus.UNAUTHORIZED)
public String sessionInvalid() {
    return "session已失效,请重新认证";
}

为了演示,我们将Session的超时时间设置为最小值60秒,重启项目,认证后等待60秒并刷新页面:

image-20220425195046708

可看到请求跳转到了我们自定义的/session/invalidURL上。

2. Session并发控制

Session并发控制可以控制一个账号同一时刻最多能登录多少个。我们在Spring Security配置中继续添加Session相关配置:

// session 管理
    @Autowired
    private MySessionExpiredStrategy sessionExpiredStrategy;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class) // 添加验证码校验过滤器
                .addFilterBefore(smsCodeFilter, UsernamePasswordAuthenticationFilter.class) // 添加短信验证码校验过滤器
                .formLogin() // 表单方式
                //http.httpBasic() // HTTP Basic方式
//                .loginPage("/login.html")
                .loginPage("/authentication/require") // 登录跳转 URL
                .loginProcessingUrl("/login")
                .successHandler(authenticationSuccessHandler) // 处理登录成功
                .failureHandler(authenticationFailureHandler) // 处理登录失败
                .and()
                .rememberMe()
                .tokenRepository(persistentTokenRepository()) // 配置 token 持久化仓库
                .tokenValiditySeconds(3600) // remember 过期时间,单为秒
                .userDetailsService(userDetailService) // 处理自动登录逻辑
                .and()
                .authorizeRequests() // 授权配置
                .antMatchers("/authentication/require", "/login.html", "/code/image", "/code/sms", "/session/invalid").permitAll()
                .anyRequest()  // 所有请求
                .authenticated() // 都需要认证
                .and()
                .sessionManagement() // 添加 Session管理器
                .invalidSessionUrl("/session/invalid") // Session失效后跳转到这个链接
                .maximumSessions(1)
                .expiredSessionStrategy(sessionExpiredStrategy)
                .and()
                .and().csrf().disable()
                .apply(smsAuthenticationConfig); // 将短信验证码认证配置加到 Spring Security 中
    }

maximumSessions配置了最大Session并发数量为1个,如果cc这个账户登录后,在另一个客户端也使用cc账户登录,那么第一个使用cc登录的账户将会失效,类似于一个先入先出队列。expiredSessionStrategy配置了Session在并发下失效后的处理策略,这里为我们自定义的策略MySessionExpiredStrategy

MySessionExpiredStrategy实现SessionInformationExpiredStrategy

@Component
public class MySessionExpiredStrategy implements SessionInformationExpiredStrategy {

    @Override
    public void onExpiredSessionDetected(SessionInformationExpiredEvent event) throws IOException, ServletException {
        HttpServletResponse response = event.getResponse();
        response.setStatus(HttpStatus.UNAUTHORIZED.value());
        response.setContentType("application/json;charset=utf-8");
        response.getWriter().write("您的账号已经在别的地方登录,当前登录已失效。如果密码遭到泄露,请立即修改密码!");
    }
    
}

为了演示这个效果,我们先将Session超时时间设置久一点,比如3600秒,然后重启项目,在Chrome里使用cc账户登录。

登录成功后,在firefox上也是用cc账户登录,登录成功后回到chrome,刷新页面,效果如下所示:

image-20220425201756232

除了后者将前者踢出的策略,我们也可以控制当Session达到最大有效数的时候,不再允许相同的账户登录。

要实现这个功能只需要在上面的配置中添加:

.and()
    .sessionManagement() // 添加 Session管理器
    .invalidSessionUrl("/session/invalid") // Session失效后跳转到这个链接
    .maximumSessions(1)
    .maxSessionsPreventsLogin(true)
    .expiredSessionStrategy(sessionExpiredStrategy)
    .and()

重启系统,在chrome上登录mrbird账户后,在firefox上尝试使用mrbird账户登录:

image-20220425201950390

可以看到登录受限。

在实际开发中,发现Session并发控制只对Spring Security默认的登录方式——账号密码登录有效,而像短信验证码登录,社交账号登录并不生效.

3. 其他操作

SessionRegistry包含了一些使用的操作Session的方法,比如:

  1. 踢出用户(让Session失效):

    String currentSessionId = request.getRequestedSessionId();
    sessionRegistry.getSessionInformation(sessionId).expireNow();
    
  2. 获取所有Session信息:

    List<Object> principals = sessionRegistry.getAllPrincipals();
    

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/16707.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!