Spring Security的一个常用配置就是检测相同的用户以不同的session登录系统。这称为concurrency control,是session管理一系列配置的一部分。Spring Security的session管理能够以两种不同的方式进行配置——session fixation protection(固化保护)和concurrency control(并发控制)。
配置session fixation保护
Session fixation protection是恶意用户试图窃取系统中一个未认证用户的session,在用户认证后,创建session副本,无需用户名和密码来访问用户信息的保护措施。攻击流程如图所示。
该类攻击的主要是利用了用户认证前后使用相同的session这个漏洞,其保护措施就是在用户认证后创建一个新的session,并令旧session失效。
Spring Security默认已经开启了Session Fixation Protection。下面的配置等同于默认开启设置
@Override
protected void configure(HttpSecurity http) throws Exception {
http
//Spring Security的默认启用防止固化session攻击
.and().sessionManagement().sessionFixation().migrateSession();
}
sessionFixation可以设置三种方式:
- NONE,不启用Fixation保护
- migrateSession,启用Fixation保护,用户认证后创建新的session,并将旧session的属性复制到新session中。
- newSession,启用Fixation保护,用户认证后创建新的session,但是不复制旧session的属性。
配置session concurrency protection
设置了session fixation protection,自然会想到控制session的并发数量。session并发控制能够确保一个用户不能同时拥有超过一定数量的活跃session。配置Spring Security的session并发数非常简单:
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/assets/**").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/**").hasRole("USER")
.and().formLogin().loginPage("/login.jsp").permitAll().loginProcessingUrl("/login")
.and().logout().permitAll()
//自动识别tokenRepository类型,启用PersistentTokenBasedRememberMeServices
.and().rememberMe().tokenRepository(persistentTokenRepository())
//Spring Security的默认启用防止固化session攻击
.and().sessionManagement().sessionFixation().migrateSession()
//设置session最大并发数为1,当建立新session时,原session将expired,并且跳转到登录界面
.maximumSessions(1).expiredUrl("/login.jsp").sessionRegistry(sessionRegistry).and()
.and().csrf().disable();
}
这里设置session并发数为1,当用户在session失效前又创建另一session,则前一个session失效,并且再次访问时,自动跳转到/loging.jsp,要求用户再次认证。
获取系统在线用户总数
Spring Security会将认证的用户存储到Session Registry中,通过该类就可以获取当前在线用户数据,配置方法同上。Session Registry依赖HttpSessionEventPublisher,该类注册到服务容器中,就可以监听session状态的变化,而调整Session Registry。
配置HttpSessionEventPublisher:
/**
* 以war包形式部署到web容器的启动类
*/
public class ApplicationServletInitializer extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(ApplicationInitializer.class);
}
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
super.onStartup(servletContext);
servletContext.addListener(new HttpSessionEventPublisher());
}
}
为了获得当前用户信息,还需要配置一个Spring Registry Bean,默认Spring Security并未提供该类型的bean,我们需要明确声明,并将其配置给sessionManager()。
@Bean
public SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
这样我们就可以将SessionRegistry注入到需要的地方,获取其提供的在线用户信息。
代码示例:https://github.com/wexgundam/spring.security/tree/master/ch13