在使用Spring Security框架過程中,經常會有這樣的需求,即在登錄驗證時,附帶增加額外的數據,如驗證碼、用戶類型等。下面將介紹如何實現。
注:我的工程是在Spring Boot框架基礎上的,使用xml方式配置的話請讀者自行研究吧。
- 實現自定義的WebAuthenticationDetails
該類提供了獲取用戶登錄時攜帶的額外信息的功能,默認實現WebAuthenticationDetails提供了remoteAddress與sessionId信息。開發者可以通過Authentication的getDetails()獲取WebAuthenticationDetails。我們編寫自定義類CustomWebAuthenticationDetails繼承自WebAuthenticationDetails,添加我們關心的數據(以下是一個token字段)。
package com.cgs.courses.service; import javax.servlet.http.HttpServletRequest; import org.springframework.security.web.authentication.WebAuthenticationDetails; public class CustomWebAuthenticationDetails extends WebAuthenticationDetails { /** * */ private static final long serialVersionUID = 6975601077710753878L; private final String token; public CustomWebAuthenticationDetails(HttpServletRequest request) { super(request); token = request.getParameter("token"); } public String getToken() { return token; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(super.toString()).append("; Token: ").append(this.getToken()); return sb.toString(); } }
注:在登錄頁面,可將token字段放在form表單中,也可以直接加在url的參數中,進而把額外數據發送給后台。
- 實現自定義的AuthenticationDetailsSource
該接口用於在Spring Security登錄過程中對用戶的登錄信息的詳細信息進行填充,默認實現是WebAuthenticationDetailsSource,生成上面的默認實現WebAuthenticationDetails。我們編寫類實現AuthenticationDetailsSource,用於生成上面自定義的CustomWebAuthenticationDetails。
package com.cgs.courses.service;
import javax.servlet.http.HttpServletRequest;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
import org.springframework.stereotype.Component;
@Component
public class CustomAuthenticationDetailsSource implements AuthenticationDetailsSource<HttpServletRequest, WebAuthenticationDetails> {
@Override
public WebAuthenticationDetails buildDetails(HttpServletRequest context) {
return new CustomWebAuthenticationDetails(context);
}
}
- 配置使用自定義的AuthenticationDetailsSource
只要看這一句.formLogin().authenticationDetailsSource(authenticationDetailsSource)
@Autowired private AuthenticationDetailsSource<HttpServletRequest, WebAuthenticationDetails> authenticationDetailsSource; protected void configure(HttpSecurity http) throws Exception { http .headers() .cacheControl() .contentTypeOptions() .httpStrictTransportSecurity() .xssProtection() .and() .authorizeRequests() .antMatchers( "/css/**", "/js/**") .permitAll() .antMatchers("/**") .authenticated() .and() .formLogin() .loginPage("/login") .permitAll() .defaultSuccessUrl("/todo.html", true) .authenticationDetailsSource(authenticationDetailsSource) .and() .logout() .logoutUrl("/logout") .logoutRequestMatcher(new AntPathRequestMatcher("/logout")) .logoutSuccessUrl("/login") .and() .csrf().disable(); }
- 實現自定義的AuthenticationProvider
AuthenticationProvider提供登錄驗證處理邏輯,我們實現該接口編寫自己的驗證邏輯。
package com.cgs.courses.service; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.stereotype.Component; @Component public class CustomAuthenticationProvider implements AuthenticationProvider { @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { CustomWebAuthenticationDetails details = (CustomWebAuthenticationDetails) authentication.getDetails(); // 如上面的介紹,這里通過authentication.getDetails()獲取詳細信息 // System.out.println(details); details.getRemoteAddress(); details.getSessionId(); details.getToken();
// 下面是驗證邏輯,驗證通過則返回UsernamePasswordAuthenticationToken,
// 否則,可直接拋出錯誤(AuthenticationException的子類,在登錄驗證不通過重定向至登錄頁時可通過session.SPRING_SECURITY_LAST_EXCEPTION.message獲取具體錯誤提示信息)
if (驗證通過) {
return UsernamePasswordAuthenticationToken(省略參數);
} else {
throw new AuthenticationException的子類("你要顯示的錯誤信息")
} } @Override public boolean supports(Class<?> authentication) { return authentication.equals(UsernamePasswordAuthenticationToken.class); } }
- 配置使用自定義的AuthenticationProvider
@Autowired
private AuthenticationProvider authenticationProvider;
@Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth.authenticationProvider(authenticationProvider); }