Spring Security(十):Session管理

什么是Session?我们都知道Session是服务器端记录用户状态的机制,用户是否登录、是否被挤兑、是否限制登录、登录状态是否过期等等都是关于它的事,那么Spring Security中是如何管理Session的呢,一起来了解下吧。

Session超时处理

问题描述

  • 不同的项目对于登录超时时间是不同的,比如有的七天或者一个月不操作,后台会把登录会话设为过期,有的甚至一个小时不操作,就会把登录会话设为过期,比如说一些金融APP,这么做也是为了提高安全性。所以这里要处理两个问题,一个是需要知道如何控制超时时间,另一个是会话过期之后,如果再进行一个操作,该如何处理

设置超时时间

  • 只需要在application.properties文件加上这么一句
spring.session.timeout = 10
  • 单位是秒,不设置默认是30分钟,这里设置了10秒,所以讲道理10秒不调用接口之后应该会需要重新登录了,但是事实上10秒之后session并没有失效,是什么原因呢?来看一段源码
    private long getSessionTimeoutInMinutes() {
    
    
        long sessionTimeout = (long)this.getSessionTimeout();
        if (sessionTimeout > 0L) {
    
    
            sessionTimeout = Math.max(TimeUnit.SECONDS.toMinutes(sessionTimeout), 1L);
        }

        return sessionTimeout;
    }
  • org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory#configureSession方法中,有获取Session过期时间的调用,调用的就是以上代码,它会判断配置的时间和1分钟做比较,如果比一分钟小,过期时间就取一分钟,也就是说最短过期时间就是一分钟,我们上面配置的10秒钟是不生效的。

配置Session过期后页面跳转

  • 还是在我们主配置类BrowserSecurityConfig中加上页面跳转配置路径
	protected void configure(HttpSecurity http) throws Exception {
    
    
		http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)
				.formLogin()
				.loginPage("/authentication/require")
				.loginProcessingUrl("/authentication/form")
				.successHandler(meicloudAuthenticationSuccessHandler)
				.failureHandler(meicloudAuthenticationFailureHandler)
				.and()
				.rememberMe()
				.tokenRepository(persistentTokenRepository())
				.tokenValiditySeconds(3600)
				.userDetailsService(userDetailsService)
				.and()
				// 配置Session过期之后的路径跳转
				.sessionManagement()
				.invalidSessionUrl("/session/invalid")
				.and()
				.authorizeRequests()
				.antMatchers("/authentication/require", securityProperties.getBrowser().getSignInPage(), "/code/*", "/session/invalid").permitAll()
				.anyRequest()
				.authenticated()
				.and()
				.csrf().disable()
				.apply(smsCodeAuthenticationSecurityConfig);
	}

Session并发控制

问题描述

  • 所谓并发控制就是用户在这个手机登录后,他又在另一个手机登录相同账户,对于之前登录的账户是否需要被挤兑,或者说在第二次登录时限制它登录,更或者像腾讯视频VIP账号一样,最多只能五个人同时登录,第六个人限制登录,看看这些效果Spring Security是如何实现的?

挤兑登录(后面登录把前面登录的用户Session失效掉)

  • 也是在主配置类中配置一个maxmumSessions属性
	protected void configure(HttpSecurity http) throws Exception {
    
    
		http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)
				.formLogin()
				.loginPage("/authentication/require")
				.loginProcessingUrl("/authentication/form")
				.successHandler(meicloudAuthenticationSuccessHandler)
				.failureHandler(meicloudAuthenticationFailureHandler)
				.and()
				.rememberMe()
				.tokenRepository(persistentTokenRepository())
				.tokenValiditySeconds(3600)
				.userDetailsService(userDetailsService)
				.and()
				// Session相关配置
				.sessionManagement()
				// 配置Session过期之后的路径跳转
				.invalidSessionUrl("/session/invalid")
				// 配置最大Session数量,配置为1表示后面登录的会把前面登录的用户挤兑
				.maxmumSessions(1)
				// 配置挤兑后处理类
				.expireSessionStrategy(new MeicloudExpiredSessionStrategy())
				.and()
				.authorizeRequests()
				.antMatchers("/authentication/require", securityProperties.getBrowser().getSignInPage(), "/code/*", "/session/invalid").permitAll()
				.anyRequest()
				.authenticated()
				.and()
				.csrf().disable()
				.apply(smsCodeAuthenticationSecurityConfig);
	}
  • 配置上一个用户被挤兑之后的处理类,实现SessionInformationExpiredStrategy接口,然后还需要在主配置类添加expireSessionStrategy将处理类配置上去。
public class MeicloudExpiredSessionStrategy implements SessionInformationExpiredStrategy {
    
    
	@Override
	public void onExpiredSessionDetected(SessionInformationExpiredEvent event) throws IOException, ServletException {
    
    
		event.getResponse().getWriter().write("您已被挤兑下线!");
	}
}

限制登录(登录达到一定数量后,限制后续登录)

  • 就是说前面用户登录达到配置的数值之后,后面的用户无法继续登录,需要在主配置类配置maxSessionPreventsLogin属性
	protected void configure(HttpSecurity http) throws Exception {
    
    
		http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)
				.formLogin()
				.loginPage("/authentication/require")
				.loginProcessingUrl("/authentication/form")
				.successHandler(meicloudAuthenticationSuccessHandler)
				.failureHandler(meicloudAuthenticationFailureHandler)
				.and()
				.rememberMe()
				.tokenRepository(persistentTokenRepository())
				.tokenValiditySeconds(3600)
				.userDetailsService(userDetailsService)
				.and()
				// Session相关配置
				.sessionManagement()
				// 配置Session过期之后的路径跳转
				.invalidSessionUrl("/session/invalid")
				// 配置最大Session数量,配置为1表示后面登录的会把前面登录的用户挤兑
				.maxmumSessions(1)
				// 将该属性配置成true表示限制最大登录用户数量,上面配置的数值是1,表示只能登录一次用户
				.maxSessionPreventsLogin(true)
				// 配置挤兑后处理类
				.expireSessionStrategy(new MeicloudExpiredSessionStrategy())
				.and()
				.authorizeRequests()
				.antMatchers("/authentication/require", securityProperties.getBrowser().getSignInPage(), "/code/*", "/session/invalid").permitAll()
				.anyRequest()
				.authenticated()
				.and()
				.csrf().disable()
				.apply(smsCodeAuthenticationSecurityConfig);
	}

集群Session管理

问题描述

  • 默认情况下,Session是放在中间服务器里面的,比如说tomcat,当应用集群部署同时没有做其他同步Session处理的话,就会出现用户在A机器登录,这时会把登录信息放在A机器Session上,而后续很多请求是请求B机器,B机器并没有登录信息,于是B机器仍然会要求用户再次登录,这显然不合理,所以这种情况来看看可以如何处理?

处理方案

  • Session是基于服务器内存,不支持集群,而Session目的是存放登录信息,要支持集群的话,可以把登录信息保存到分布式集群的存储结构上,比如Redis中。
  • 原始Session和分布式Session流程图
    Session信息存储图

实际操作

  • 引入jar包
	<dependency>
		<groupId>org.springframework.session</groupId>
		<artifactId>spring-session</artifactId>
	</dependency>
  • Spring支持的Session存储类型,在StoreType枚举类里面,这里建议放在Redis中,一个原因是获取登录信息非常频繁,正好Redis获取速度快,另一个Session有过期时间,而Redis天生就支持设置Key的过期限制。
public enum StoreType {
    
    
    REDIS,
    MONGO,
    JDBC,
    HAZELCAST,
    HASH_MAP,
    NONE;
    private StoreType() {
    
    
    }
}
  • 配置存储类型,在application.properties文件配置,不需要其他代码,就已经支持将登录信息存放到Redis了,可以启多个端口进行一个测试。
spring.session.store-type = REDIS

猜你喜欢

转载自blog.csdn.net/qq_36221788/article/details/106393410