學習本章之前,可以先了解下上篇Spring Security認證配置(二)
本篇想要達到這樣幾個目的:
1、登錄成功處理
2、登錄失敗處理
3、調用方自定義登錄后處理類型
具體配置代碼如下:
spring-security-browser
登錄成功處理:
/** * 自定義登錄成功后處理 */ @Slf4j @Component public class LoginSuccessHandler implements AuthenticationSuccessHandler { @Autowired private ObjectMapper objectMapper; @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { log.info("登錄成功..."); response.setContentType("application/json;charset=UTF-8"); // 返回authentication 認證信息 response.getWriter().write(objectMapper.writeValueAsString(authentication)); } }
這段配置表示,登錄成功后,會向response中塞入authentication對象
代碼解析:
點開 AuthenticationSuccessHandler 類,可以看到里面只有一個onAuthenticationSuccess方法,如下:

在onAuthenticationSuccess方法中,有個Authentication對象,其用法可參考AuthenticationManager、ProviderManager
登錄失敗處理:
/** * 自定義登錄失敗后處理 */ @Slf4j @Component public class LoginFailureHandler implements AuthenticationFailureHandler { @Autowired private ObjectMapper objectMapper; @Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { log.info("登錄失敗"); response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); // 服務器內部異常500 response.setContentType("application/json;charset=UTF-8"); // 返回異常信息 response.getWriter().write(objectMapper.writeValueAsString(exception)); } }
這段配置表示,登錄失敗后,會向response中塞入AuthenticationException 對象,並返回500狀態碼
代碼解析:
在 AuthenticationFailureHandler 中也只有一個onAuthenticationFailure方法,其入參 AuthenticationException 是認證授權過程中拋出的異常類的基類,
有以下幾個子類:

比較常用的是 BadCredentialsException(密碼錯誤)、UsernameNotFoundException(用戶名找不到)等等
修改下SecurityConfig類中的配置:
@Autowired private SecurityProperty securityProperty; @Autowired private LoginSuccessHandler loginSuccessHandler; //登錄成功處理 @Autowired private LoginFailureHandler loginFailureHandler; //登錄失敗處理 @Override protected void configure(HttpSecurity http) throws Exception { // formLogin()是默認的登錄表單頁,如果不配置 loginPage(url),則使用 spring security // 默認的登錄頁,如果配置了 loginPage()則使用自定義的登錄頁 http.formLogin() // 表單登錄 .loginPage(SecurityConst.AUTH_REQUIRE) .loginProcessingUrl(SecurityConst.AUTH_FORM) // 登錄請求攔截的url,也就是form表單提交時指定的action .successHandler(loginSuccessHandler) .failureHandler(loginFailureHandler) .and() .authorizeRequests() // 對請求授權 .antMatchers(SecurityConst.AUTH_REQUIRE, securityProperty.getBrowser().getLoginPage()).permitAll() // 允許所有人訪問login.html和自定義的登錄頁 .anyRequest() // 任何請求 .authenticated()// 需要身份認證 .and() .csrf().disable() // 關閉跨站偽造 ; }
啟動服務,訪問 http://localhost:18081/index.html,跳轉到登錄頁,輸入正確的用戶名密碼(xwj/1234),跳轉到如下頁面:

查看控制台,可以看到Authentication對象包含的數據:

清掉瀏覽器緩存,然后再輸入錯誤的密碼,跳轉到如下頁面:

查看控制台,可以看到AuthenticationException對象包含的數據:

上面配置了登錄成功和失敗的處理,但是只能夠返回JSON格式的數據。如果希望返回一個頁面或者是跳轉,那就可以使用Security默認的處理方式
在Security中,默認的成功跟失敗處理分別是 SavedRequestAwareAuthenticationSuccessHandler 與 SimpleUrlAuthenticationFailureHandler
通過分析 AbstractAuthenticationProcessingFilter 類,可以看到:

在服務啟動時,如果在我們的配置類中,沒有配置這AuthenticationSuccessHandler跟AuthenticationFailureHandler,則使用這兩個默認的。如果配置了,則使用配置中
定義的登錄處理類。打個斷點可以看到:


修改下上面的配置代碼:
spring-security-browser
為了達到調用方自定義跳轉方式,增加配置類,如下:
/** * 登錄類型枚舉 */ public enum LoginType { REDIRECT, JSON }
修改BrowserProperty,增加默認的登錄類型(JSON):
@Getter @Setter public class BrowserProperty { private String loginPage = "/login.html"; // 登錄頁 private LoginType loginType = LoginType.JSON; //登錄類型 }
接下來修改登錄與失敗處理類:
/** * 自定義登錄成功后處理 * (繼承SavedRequestAwareAuthenticationSuccessHandler,這是默認的成功處理器,其帶有requestCache) */ @Slf4j @Component public class LoginSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler { @Autowired private ObjectMapper objectMapper; @Autowired private SecurityProperty securityProperty; @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { log.info("登錄成功..."); if (LoginType.JSON.equals(securityProperty.getBrowser().getLoginType())) { // json請求(如ajax) response.setContentType("application/json;charset=UTF-8"); // 返回authentication 認證信息 response.getWriter().write(objectMapper.writeValueAsString(authentication)); } else { // 跳轉 super.onAuthenticationSuccess(request, response, authentication); } } }
/** * 自定義登錄失敗后處理 * (繼承SimpleUrlAuthenticationFailureHandler,這是默認的失敗處理器) */ @Slf4j @Component public class LoginFailureHandler extends SimpleUrlAuthenticationFailureHandler { @Autowired private ObjectMapper objectMapper; @Autowired private SecurityProperty securityProperty; @Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { log.info("登錄失敗"); if (LoginType.JSON.equals(securityProperty.getBrowser().getLoginType())) { // json請求(如ajax) response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); // 服務器內部異常500 response.setContentType("application/json;charset=UTF-8"); // 返回異常信息 response.getWriter().write(objectMapper.writeValueAsString(exception)); } else { super.onAuthenticationFailure(request, response, exception); } } }
spring-security-demo
在配置文件(application.yml)中,加上:
security: browser: #loginPage: /plogin.html loginType: REDIRECT
重新啟動服務,訪問 http://localhost:18081/index.html,跳轉到登錄頁,輸入正確的用戶名密碼(xwj/1234),跳轉到具體的頁面:

清掉瀏覽器緩存,然后再輸入錯誤的密碼,跳轉到如下頁面:

從上面可以看到,返回401狀態碼,類型為Unauthorized,表示未認證。這里的????其實是認證失敗原因,打個斷點可以看到:

拋出了BadCredentialsException異常,"壞的憑證"表示密碼錯誤
