Spring Security的一個(gè)常用配置就是檢測(cè)相同的用戶(hù)以不同的session登錄系統(tǒng)。這稱(chēng)為concurrency control骏啰,是session管理一系列配置的一部分。Spring Security的session管理能夠以?xún)煞N不同的方式進(jìn)行配置——session fixation protection(固化保護(hù))和concurrency control(并發(fā)控制)抽高。
配置session fixation保護(hù)
Session fixation protection是惡意用戶(hù)試圖竊取系統(tǒng)中一個(gè)未認(rèn)證用戶(hù)的session判耕,在用戶(hù)認(rèn)證后,創(chuàng)建session副本翘骂,無(wú)需用戶(hù)名和密碼來(lái)訪(fǎng)問(wèn)用戶(hù)信息的保護(hù)措施壁熄。攻擊流程如圖所示。
該類(lèi)攻擊的主要是利用了用戶(hù)認(rèn)證前后使用相同的session這個(gè)漏洞碳竟,其保護(hù)措施就是在用戶(hù)認(rèn)證后創(chuàng)建一個(gè)新的session草丧,并令舊session失效。
Spring Security默認(rèn)已經(jīng)開(kāi)啟了Session Fixation Protection莹桅。下面的配置等同于默認(rèn)開(kāi)啟設(shè)置
@Override
protected void configure(HttpSecurity http) throws Exception {
http
//Spring Security的默認(rèn)啟用防止固化session攻擊
.and().sessionManagement().sessionFixation().migrateSession();
}
sessionFixation可以設(shè)置三種方式:
- NONE昌执,不啟用Fixation保護(hù)
- migrateSession,啟用Fixation保護(hù)诈泼,用戶(hù)認(rèn)證后創(chuàng)建新的session懂拾,并將舊session的屬性復(fù)制到新session中。
- newSession铐达,啟用Fixation保護(hù)岖赋,用戶(hù)認(rèn)證后創(chuàng)建新的session,但是不復(fù)制舊session的屬性瓮孙。
配置session concurrency protection
設(shè)置了session fixation protection唐断,自然會(huì)想到控制session的并發(fā)數(shù)量汁汗。session并發(fā)控制能夠確保一個(gè)用戶(hù)不能同時(shí)擁有超過(guò)一定數(shù)量的活躍session。配置Spring Security的session并發(fā)數(shù)非常簡(jiǎn)單:
@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()
//自動(dòng)識(shí)別tokenRepository類(lèi)型栗涂,啟用PersistentTokenBasedRememberMeServices
.and().rememberMe().tokenRepository(persistentTokenRepository())
//Spring Security的默認(rèn)啟用防止固化session攻擊
.and().sessionManagement().sessionFixation().migrateSession()
//設(shè)置session最大并發(fā)數(shù)為1,當(dāng)建立新session時(shí)祈争,原session將expired斤程,并且跳轉(zhuǎn)到登錄界面
.maximumSessions(1).expiredUrl("/login.jsp").sessionRegistry(sessionRegistry).and()
.and().csrf().disable();
}
這里設(shè)置session并發(fā)數(shù)為1,當(dāng)用戶(hù)在session失效前又創(chuàng)建另一session菩混,則前一個(gè)session失效忿墅,并且再次訪(fǎng)問(wèn)時(shí),自動(dòng)跳轉(zhuǎn)到/loging.jsp沮峡,要求用戶(hù)再次認(rèn)證疚脐。
獲取系統(tǒng)在線(xiàn)用戶(hù)總數(shù)
Spring Security會(huì)將認(rèn)證的用戶(hù)存儲(chǔ)到Session Registry中,通過(guò)該類(lèi)就可以獲取當(dāng)前在線(xiàn)用戶(hù)數(shù)據(jù)邢疙,配置方法同上棍弄。Session Registry依賴(lài)HttpSessionEventPublisher,該類(lèi)注冊(cè)到服務(wù)容器中疟游,就可以監(jiān)聽(tīng)session狀態(tài)的變化呼畸,而調(diào)整Session Registry。
配置HttpSessionEventPublisher:
/**
* 以war包形式部署到web容器的啟動(dòng)類(lèi)
*/
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());
}
}
為了獲得當(dāng)前用戶(hù)信息颁虐,還需要配置一個(gè)Spring Registry Bean蛮原,默認(rèn)Spring Security并未提供該類(lèi)型的bean,我們需要明確聲明另绩,并將其配置給sessionManager()儒陨。
@Bean
public SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
這樣我們就可以將SessionRegistry注入到需要的地方,獲取其提供的在線(xiàn)用戶(hù)信息笋籽。
代碼示例:https://github.com/wexgundam/spring.security/tree/master/ch13