本文講述一下如何自定義spring security的登陸頁,網上給的資料大多過期,並且是基於後端模板技術的,講的不是太清晰,本文給出一個採用ajax的登陸及返回的先後端分離方式。css
總共須要處理3個地方,一個是異常的處理,須要兼容ajax請求,一個是成功返回的處理,一個是失敗返回的處理。html
public class UnauthorizedEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
if(isAjaxRequest(request)){
response.sendError(HttpServletResponse.SC_UNAUTHORIZED,authException.getMessage());
}else{
response.sendRedirect("/login.html");
}
}
public static boolean isAjaxRequest(HttpServletRequest request) {
String ajaxFlag = request.getHeader("X-Requested-With");
return ajaxFlag != null && "XMLHttpRequest".equals(ajaxFlag);
}
}複製代碼
這裏咱們自定義成功及失敗的ajax返回,固然這裏咱們簡單處理,只返回statusCode前端
public class AjaxAuthSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
response.setStatus(HttpServletResponse.SC_OK);
}
}複製代碼
public class AjaxAuthFailHandler extends SimpleUrlAuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authentication failed");
}
}複製代碼
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.exceptionHandling().authenticationEntryPoint(new UnauthorizedEntryPoint())
.and()
.csrf().disable()
.authorizeRequests()
.antMatchers("/login","/css/**", "/js/**","/fonts/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login.html")
.loginProcessingUrl("/login")
.usernameParameter("name")
.passwordParameter("password")
.successHandler(new AjaxAuthSuccessHandler())
.failureHandler(new AjaxAuthFailHandler())
.permitAll()
.and()
.logout()
.logoutUrl("/logout")
.permitAll();
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("admin").password("admin").roles("USER");
}
}複製代碼
這裏有幾個要注意的點:java
- permitAll
這裏要添加前端資源路徑,以及登錄表單請求的接口地址/login- loginPage
這裏設置登陸頁面的地址,這裏咱們用靜態頁面,即static目錄下的login.html- ajax配置
將authenticationEntryPoint,successHandler,failureHandler設置爲上面自定義的ajax處理類
就是一個純粹的html頁面,其中登陸按鈕的ajax請求以下:web
$.ajax({ url: '/login', type: 'POST', data: "name="+name+"&password="+password, success: function (res, status) { window.location.href='/ok.html' }, error: function (res, status) { dangerDialog(res.statusText); } });複製代碼
這裏是請求/login,也就是spring security會默認攔截的路徑,不瞭解spring security的人可能會納悶,我請求這個路徑,可是工程裏頭沒有定義/login的request mapping,沒關係麼。下面來剖析一下。ajax
Alias | Filter Class | Namespace Element or Attribute |
---|---|---|
CHANNEL_FILTER | ChannelProcessingFilter | http/intercept-url@requires-channel |
SECURITY_CONTEXT_FILTER | SecurityContextPersistenceFilter | http |
CONCURRENT_SESSION_FILTER | ConcurrentSessionFilter | session-management/concurrency-control |
HEADERS_FILTER | HeaderWriterFilter | http/headers |
CSRF_FILTER | CsrfFilter | http/csrf |
LOGOUT_FILTER | LogoutFilter | http/logout |
X509_FILTER | X509AuthenticationFilter | http/x509 |
PRE_AUTH_FILTER | AbstractPreAuthenticatedProcessingFilter Subclasses | N/A |
CAS_FILTER | CasAuthenticationFilter | N/A |
FORM_LOGIN_FILTER | UsernamePasswordAuthenticationFilter | http/form-login |
BASIC_AUTH_FILTER | BasicAuthenticationFilter | http/http-basic |
SERVLET_API_SUPPORT_FILTER | SecurityContextHolderAwareRequestFilter | http/@servlet-api-provision |
JAAS_API_SUPPORT_FILTER | JaasApiIntegrationFilter | http/@jaas-api-provision |
REMEMBER_ME_FILTER | RememberMeAuthenticationFilter | http/remember-me |
ANONYMOUS_FILTER | AnonymousAuthenticationFilter | http/anonymous |
SESSION_MANAGEMENT_FILTER | SessionManagementFilter | session-management |
EXCEPTION_TRANSLATION_FILTER | ExceptionTranslationFilter | http |
FILTER_SECURITY_INTERCEPTOR | FilterSecurityInterceptor | http |
SWITCH_USER_FILTER | SwitchUserFilter | N/A |
這裏咱們要關注的就是這個UsernamePasswordAuthenticationFilter,顧名思義,它是filter,在執行/login請求的時候攔截,於是是不須要工程裏頭去定義login的request mapping的。spring
spring-security-web-4.2.3.RELEASE-sources.jar!/org/springframework/security/web/authentication/UsernamePasswordAuthenticationFilter.javasegmentfault
public class UsernamePasswordAuthenticationFilter extends
AbstractAuthenticationProcessingFilter {
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
if (postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException(
"Authentication method not supported: " + request.getMethod());
}
String username = obtainUsername(request);
String password = obtainPassword(request);
if (username == null) {
username = "";
}
if (password == null) {
password = "";
}
username = username.trim();
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
username, password);
// Allow subclasses to set the "details" property
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
//......
}複製代碼
這裏就是攔截,獲取login.html提交的參數,而後交給authenticationManager去認證。以後就是走後續的filter,若是成功,則會進行相應的session配置。後端