第三章已經可以從數據庫查出密碼進行各種校驗了,但是對於各種登錄認證流程來說還是單調,無法滿足我們的企業級開發需求,本篇就進行一個個性化用戶認證流程處理,包括自定義登錄頁面,自定義登錄成功處理,自定義登陸失敗處理。
自定義登錄頁面
自定義的登錄頁:meicloud-signIn.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>登錄</title>
</head>
<body>
<h2>標準登錄頁面</h2>
<h3>表單登錄</h3>
<form action="/authentication/form" method="post">
<table>
<tr>
<td>用戶名:</td>
<td><input type="text" name="username"></td>
</tr>
<tr>
<td>密碼:</td>
<td><input type="password" name="password"></td>
</tr>
<tr>
<td colspan="2"><button type="submit">登錄</button></td>
</tr>
</table>
</form>
</body>
</html>
配置自定義登錄頁
- 在我們前面篇章講到的 BrowserSecurityConfig 配置類中進行配置,如下
.loginPage()
配置項
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
// 使用自定義的表單登錄頁面
.loginPage("/meicloud-signIn.html")
// 以下這行 UsernamePasswordAuthenticationFilter 會知道要處理表單的 /authentication/form 請求,而不是默認的 /login
.loginProcessingUrl("/authentication/form")
.and()
.authorizeRequests()
// 排除對 "/meicloud-signIn.html" 的身份驗證
.antMatchers("/meicloud-signIn.html").permitAll()
// 表示所有請求都需要身份驗證
.anyRequest()
.authenticated()
.and()
.csrf().disable();// 暫時把跨站請求僞造的功能關閉掉
}
.loginProcessingUr()
表示要處理表單的/authentication/form
登錄請求,而不是默認的/login
- 要注意的是,
.anyRequest
表示所有請求都需要身份驗證,導致我們的登錄頁/meicloud-signIn.html
也需要進行身份驗證,所以我們需要配置.antMatchers("/meicloud-signIn.html").permitAll()
來使得登錄頁不需要進行攔截認證。
自定義登錄成功處理
實現AuthenticationSuccessHandler
接口,實現onAuthenticationSuccess()
方法
- 但是一般情況下不會去直接實現該接口,一般可以繼承該接口的實現類,如下,這樣使得給到前端的請求有更多種選擇,比如JSON或者是跳到另一個路徑。
@Component("meicloudAuthenticationSuccessHandler")
public class MeicloudAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
private Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private ObjectMapper objectMapper;
@Autowired
private SecurityProperties securityProperties;
private RequestCache requestCache = new HttpSessionRequestCache();
/**
* 登錄成功處理
* @param authentication 封裝認證信息,包括認證請求信息,ip、session信息等,還包括認證通過以後,自定義的UserDetailService返回的UserDetails信息
*/
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
logger.info("登錄成功");
if (LoginResponseType.JSON.equals(securityProperties.getBrowser().getSignInResponseType())) {
response.setContentType("application/json;charset=UTF-8");
String type = authentication.getClass().getSimpleName();
response.getWriter().write(objectMapper.writeValueAsString(new SimpleResponse(type)));
} else {
// 如果設置了meicloud.security.browser.singInSuccessUrl,總是跳到設置的地址上
// 如果沒設置,則嘗試跳轉到登錄之前訪問的地址上,如果登錄前訪問地址爲空,則跳到網站根路徑上
if (StringUtils.isNotBlank(securityProperties.getBrowser().getSingInSuccessUrl())) {
requestCache.removeRequest(request, response);
setAlwaysUseDefaultTargetUrl(true);
setDefaultTargetUrl(securityProperties.getBrowser().getSingInSuccessUrl());
}
super.onAuthenticationSuccess(request, response, authentication);
}
}
}
- 然後需要在
BrowserSecurityConfig
配置類中進行配置,加上一行.successHandler()
配置
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
.loginPage("/meicloud-signIn.html")
.loginProcessingUrl("/authentication/form")
// 配置成功處理
.successHandler(meicloudAuthenticationSuccessHandler)
.and()
.authorizeRequests()
.antMatchers("/meicloud-signIn.html").permitAll()
.anyRequest()
.authenticated()
.and()
.csrf().disable();
}
自定義登陸失敗處理
和登錄成功處理類似,實現 AuthenticationFailHandler 接口,實現onAuthenticationFailure()
方法
- 同理一樣不會去直接實現該接口,可以繼承它的實現類
SimpleUrlAuthenticationFailureHandler
@Component("meicloudAuthenctiationFailureHandler")
class MeicloudAuthencationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
private Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private ObjectMapper objectMapper;
@Autowired
private SecurityProperties securityProperties;
/**
* 登錄失敗處理
* @param exception 包含了登錄錯誤時的異常,包括用戶名沒找着、密碼不正確等等
*/
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
logger.info("登錄失敗");
if (LoginResponseType.JSON.equals(securityProperties.getBrowser().getSignInResponseType())) {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(objectMapper.writeValueAsString(new SimpleResponse(exception.getMessage())));
}else{
super.onAuthenticationFailure(request, response, exception);
}
}
}
- 同樣需要添加在
BrowserSecurityConfig
中添加.failureHandler()
配置
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
.loginPage("/meicloud-signIn.html")
.loginProcessingUrl("/authentication/form")
// 配置成功處理
.successHandler(meicloudAuthenticationSuccessHandler)
.failureHandler(meicloudAuthenticationFailureHandler)
.and()
.authorizeRequests()
.antMatchers("/meicloud-signIn.html").permitAll()
.anyRequest()
.authenticated()
.and()
.csrf().disable();
}
總結
需求 | 實現 |
---|---|
自定義登錄頁面 | http.formLogin().loginPage("/meicloud-signIn.html") |
自定義登錄成功處理 | AuthenticationSuccessHandler |
自定義登錄失敗處理 | AuthenticationFailureHandler |