我對安全性感到陌生,並且遇到了導致用戶帳戶被鎖定的問題,只有應用程序重新啓動才能修復它。Spring Security以併發登錄嘗試鎖定用戶
我有一個春季開機(1.3.0.BUILD-SNAPSHOT)與彈簧安全(4.0.2.RELEASE)應用程序,我試圖控制併發會話策略,因此用戶只能有一個登錄。它能夠正確檢測到來自其他瀏覽器的後續登錄嘗試並阻止該嘗試但是,我注意到一些奇怪的行爲,我似乎無法追查:
- 用戶可以在同一瀏覽器中驗證兩個選項卡。我無法用三個標籤登錄,但有兩個作品。註銷一個似乎註銷兩者。我看到了cookie值是相同的,所以我猜測他們正在共享會話:
標籤1 JSESSIONID:DA7C3EF29D55297183AF5A9BEBEF191F & 941135CEBFA92C3912ADDC1DE41CFE9A
標籤2 JSESSIONID:DA7C3EF29D55297183AF5A9BEBEF191F & 48C17A19B2560EAB8EC3FDF51B179AAE
第二次登錄嘗試提出這似乎表明第二次登錄嘗試(我踩着通春,安全源驗證以下日誌消息:
o.s.s.w.a.i.FilterSecurityInterceptor : Secure object: FilterInvocation: URL: /loginPage; Attributes: [permitAll]
o.s.s.w.a.i.FilterSecurityInterceptor : Previously Authenticated: org.springframew[email protected]754041c8: Principal: User [[email protected], password=<somevalue> ]; Credentials: [PROTECTED]; Authenticated: true; Details: org.sprin[email protected]43458: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: 4708D404F64EE758662B2B308F36FFAC; Granted Authorities: Owner
o.s.s.access.vote.AffirmativeBased : Voter: org.sp[email protected]17527bbe, returned: 1
o.s.s.w.a.i.FilterSecurityInterceptor : Authorization successful
o.s.s.w.a.i.FilterSecurityInterceptor : RunAsManager did not change Authentication object
o.s.security.web.FilterChainProxy : /loginPage reached end of additional filter chain; proceeding with original chain
org.apache.velocity : ResourceManager : unable to find resource 'loginPage.vm' in any resource loader.
o.s.s.w.a.ExceptionTranslationFilter : Chain processed normally
s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed
- 當我登錄兩個選項卡,然後註銷後,用戶帳戶被鎖定並需要重新啓動服務器。控制檯中沒有錯誤,並且數據庫中的用戶記錄未更改。
這裏是我的安全配置:
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private CustomUserDetailsService customUserDetailsService;
@Autowired
private SessionRegistry sessionRegistry;
@Autowired
ServletContext servletContext;
@Autowired
private CustomLogoutHandler logoutHandler;
@Autowired
private MessageSource messageSource;
/**
* Sets security configurations for the authentication manager
*/
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth)
throws Exception {
auth
.userDetailsService(customUserDetailsService)
.passwordEncoder(passwordEncoder());
return;
}
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin()
.loginPage("/loginPage")
.permitAll()
.loginProcessingUrl("/login")
.defaultSuccessUrl("/?tab=success")
.and()
.logout().addLogoutHandler(logoutHandler).logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.deleteCookies("JSESSIONID")
.invalidateHttpSession(true).permitAll().and().csrf()
.and()
.sessionManagement().sessionAuthenticationStrategy( concurrentSessionControlAuthenticationStrategy).sessionFixation().changeSessionId().maximumSessions(1)
.maxSessionsPreventsLogin(true).expiredUrl("/login?expired").sessionRegistry(sessionRegistry)
.and()
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.invalidSessionUrl("/")
.and().authorizeRequests().anyRequest().authenticated();
http.headers().contentTypeOptions();
http.headers().xssProtection();
http.headers().cacheControl();
http.headers().httpStrictTransportSecurity();
http.headers().frameOptions();
servletContext.getSessionCookieConfig().setHttpOnly(true);
}
@Bean
public ConcurrentSessionControlAuthenticationStrategy concurrentSessionControlAuthenticationStrategy() {
ConcurrentSessionControlAuthenticationStrategy strategy = new ConcurrentSessionControlAuthenticationStrategy(sessionRegistry());
strategy.setExceptionIfMaximumExceeded(true);
strategy.setMessageSource(messageSource);
return strategy;
}
// Work around https://jira.spring.io/browse/SEC-2855
@Bean
public SessionRegistry sessionRegistry() {
SessionRegistry sessionRegistry = new SessionRegistryImpl();
return sessionRegistry;
}
}
我也有以下幾種方法來處理檢查用戶:
@Entity
@Table(name = "USERS")
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,
property = "username")
public class User implements UserDetails {
...
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((username == null) ? 0 : username.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
User other = (User) obj;
if (username == null) {
if (other.username != null)
return false;
} else if (!username.equals(other.username))
return false;
return true;
}
}
如何防止帳戶從這樣鎖起來,或至少我如何以編程方式解鎖它們?
編輯16年1月5日 添加以下到我的WebSecurityConfig:
@Bean
public static ServletListenerRegistrationBean<HttpSessionEventPublisher> httpSessionEventPublisher() {
return new ServletListenerRegistrationBean<HttpSessionEventPublisher>(new HttpSessionEventPublisher());
}
和刪除:
servletContext.addListener(httpSessionEventPublisher())
但我仍然看到的行爲,當我登錄上兩次同一個瀏覽器 - 註銷鎖定帳戶,直到我重新啓動。
什麼Web瀏覽器是什麼呢?它發生在所有網頁瀏覽器上嗎? – JOW
我注意到了OS X Safari/Firefox/Chrome,Win IE和Linux Firefox – sonoerin
,因爲這是Spring特有的代碼問題,我認爲這會更好地問StackOverflow – schroeder