Spring security自定義用戶認(rèn)證流程詳解
1.自定義登錄頁(yè)面
(1)首先在static目錄下面創(chuàng)建login.html
注意:springboot項(xiàng)目默認(rèn)可以訪問(wèn)resources/resources,resources/staic,resources/public目錄下面的靜態(tài)文件
<!DOCTYPE html><html lang='en'><head> <meta charset='UTF-8'> <title>登錄頁(yè)面</title></head><body><form action='/auth/login' method='post'> 用戶名:<input type='text' name='username'> <br/> 密 碼:<input type='password' name='password'> <br/> <input type='submit' value='登錄'></form></body></html>
(2)在spring securiy配置類中做如下配置
@Override protected void configure(HttpSecurity http) throws Exception { http.formLogin()// 指定自定義登錄頁(yè)面.loginPage('/login.html')// 登錄url.loginProcessingUrl('/auth/login').and().authorizeRequests()// 添加一個(gè)url匹配器,如果匹配到login.html,就授權(quán).antMatchers('/login.html').permitAll().anyRequest().authenticated().and()// 關(guān)閉spring security默認(rèn)的防csrf攻擊.csrf().disable(); }
(3)測(cè)試
略
(4)存在的問(wèn)題
<1>作為可以復(fù)用的登錄模塊,我們應(yīng)該提供個(gè)性化的登錄頁(yè)面,也就是說(shuō)不能寫(xiě)死只跳轉(zhuǎn)到login.html。
此問(wèn)題比較好解決,使用可配置的登錄頁(yè)面,默認(rèn)使用login.html即可。
<2> 請(qǐng)求跳轉(zhuǎn)到login.html登錄頁(yè)面,貌似沒(méi)有什么問(wèn)題,但作為restful風(fēng)格的接口,一般響應(yīng)的都是json數(shù)據(jù)格式,尤其是app請(qǐng)求。
解決思想:用戶發(fā)起數(shù)據(jù)請(qǐng)求 --> security判斷是否需要身份認(rèn)證 ----->跳轉(zhuǎn)到一個(gè)自定義的controller方法 ------>在該方法內(nèi)判斷是否是html發(fā)起的請(qǐng)求,如果是,就跳轉(zhuǎn)到login.html,如果不是,響應(yīng)一個(gè)json格式的數(shù)據(jù),說(shuō)明錯(cuò)誤信息。
自定義Controller
@Slf4j@RestControllerpublic class LoginController { /** * 請(qǐng)求緩存 */ private RequestCache requestCache = new HttpSessionRequestCache(); /** * 重定向工具類 */ private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); /** * 如果配置的登錄頁(yè)就使用配置的登錄面,否則使用默認(rèn)的登錄頁(yè)面 */// @Value('${xxxx:defaultLoginPage}')// private String standardLoginPage; private String standardLoginPage = '/login.html'; // 登錄頁(yè) /** * 用戶身份認(rèn)證方法 */ @GetMapping('/user/auth') @ResponseStatus(code = HttpStatus.UNAUTHORIZED) // 返回狀態(tài) public ResponseData login(HttpServletRequest request, HttpServletResponse response) throws IOException { SavedRequest savedRequest = requestCache.getRequest(request, response); if (savedRequest != null) { String targetUrl = savedRequest.getRedirectUrl(); log.info('請(qǐng)求是:' + targetUrl); // 如果請(qǐng)求是以html結(jié)尾 if (StringUtils.endsWithIgnoreCase(targetUrl, '.html')) {redirectStrategy.sendRedirect(request, response, standardLoginPage); } } return new ResponseData('該請(qǐng)求需要登錄,js拿到我的響應(yīng)數(shù)據(jù)后,是否需要跳轉(zhuǎn)到登錄頁(yè)面你自己看著辦吧?'); }}
spring security給該controller的login方法授權(quán)
@Override protected void configure(HttpSecurity http) throws Exception { http.formLogin()// 先進(jìn)controller中去.loginPage('/user/auth')// 指定自定義登錄頁(yè)面.loginPage('/login.html')// 登錄url.loginProcessingUrl('/auth/login').and().authorizeRequests()// 該controller需要授權(quán).antMatchers('/user/auth').permitAll()// 添加一個(gè)url匹配器,如果匹配到login.html,就授權(quán).antMatchers('/login.html').permitAll().anyRequest().authenticated().and()// 關(guān)閉spring security默認(rèn)的防csrf攻擊.csrf().disable(); }
這樣子就行了!!!
2. 自定義登錄成功處理(返回json)
(1)實(shí)現(xiàn)AuthenticationSuccessHandler.java
import com.fasterxml.jackson.databind.ObjectMapper;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.http.MediaType;import org.springframework.security.core.Authentication;import org.springframework.security.web.authentication.AuthenticationSuccessHandler;import org.springframework.stereotype.Component;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;@Slf4j@Componentpublic class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler { @Autowired private ObjectMapper objectMapper; /** * Called when a user has been successfully authenticated. * @param request * @param response * @param authentication * @throws IOException * @throws ServletException */ @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { log.info('登錄成功!!!'); // 將登錄成功的信息寫(xiě)到前端 response.setContentType(MediaType.APPLICATION_JSON_VALUE); response.getWriter().write(objectMapper.writeValueAsString(authentication)); }}
(2)修改security配置類
@Autowired private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler; @Override protected void configure(HttpSecurity http) throws Exception { http.formLogin()// 先進(jìn)controller中去.loginPage('/user/auth')// 指定自定義登錄頁(yè)面.loginPage('/login.html')// 登錄url.loginProcessingUrl('/auth/login').successHandler(myAuthenticationSuccessHandler).and().authorizeRequests()// 該controller需要授權(quán).antMatchers('/user/auth').permitAll()// 添加一個(gè)url匹配器,如果匹配到login.html,就授權(quán).antMatchers('/login.html').permitAll().anyRequest().authenticated().and()// 關(guān)閉spring security默認(rèn)的防csrf攻擊.csrf().disable(); }
(3)測(cè)試
說(shuō)明:authentication對(duì)象中包含的信息,會(huì)因?yàn)榈卿浄绞降牟煌l(fā)生改變
3.自定義登錄失敗處理(返回json)
實(shí)現(xiàn)AuthenticationFailureHandler.java接口即可,跟登錄成敗處理配置一樣。
4.自定義登錄成功處理邏輯
以上的登錄成功或失敗的返回的都是json,但是在某些情況下,就是存在著登錄成功或者失敗進(jìn)行頁(yè)面跳轉(zhuǎn)(spring security默認(rèn)的處理方式),那么這種返回json的方式就不合適了。所以,我們應(yīng)該做得更靈活,做成可配置的。
對(duì)于登錄成功邏輯而言只需要對(duì)MyAuthenticationSuccessHandler.java稍做修改就行,代碼如下所示:
/** * SavedRequestAwareAuthenticationSuccessHandler spring security 默認(rèn)的成功處理器 */@Slf4j@Componentpublic class MyAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler { @Autowired private ObjectMapper objectMapper; /** * 配置的登錄方式 */// @Value('${xxx:默認(rèn)方式}') private String loginType = 'JSON'; /** * Called when a user has been successfully authenticated. */ @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { log.info('登錄成功!!!'); // 如果配置的登錄方式是JSON,就返回json數(shù)據(jù) if ('JSON'.equals(loginType)) { // 將登錄成功的信息寫(xiě)到前端 response.setContentType(MediaType.APPLICATION_JSON_VALUE); response.getWriter().write(objectMapper.writeValueAsString(authentication)); } else { // 否則就使用默認(rèn)的跳轉(zhuǎn)方式 super.onAuthenticationSuccess(request,response,authentication); } }}
5.自定義登錄失敗處理邏輯
同登錄成功類似,具體代碼如下:
import com.fasterxml.jackson.databind.ObjectMapper;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.http.HttpStatus;import org.springframework.http.MediaType;import org.springframework.security.core.AuthenticationException;import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;import org.springframework.stereotype.Component;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;@Slf4j@Componentpublic class MySimpleUrlAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler { @Autowired private ObjectMapper objectMapper; /** * 配置的登錄方式 */// @Value('${xxx:默認(rèn)方式}') private String loginType = 'JSON'; @Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { log.info('登錄失敗!!!'); // 如果配置的登錄方式是JSON,就返回json數(shù)據(jù) if ('JSON'.equals(loginType)) { // 將登錄成功的信息寫(xiě)到前端 response.setStatus(HttpStatus.UNAUTHORIZED.value()); response.setContentType(MediaType.APPLICATION_JSON_VALUE); response.getWriter().write(objectMapper.writeValueAsString(exception)); } else { // 否則就使用默認(rèn)的跳轉(zhuǎn)方式,跳轉(zhuǎn)到一個(gè)錯(cuò)誤頁(yè)面 super.onAuthenticationFailure(request,response,exception); } }}
@Autowired private MySimpleUrlAuthenticationFailureHandler mySimpleUrlAuthenticationFailureHandler; @Override protected void configure(HttpSecurity http) throws Exception { http.formLogin()// 先進(jìn)controller中去.loginPage('/user/auth')// 指定自定義登錄頁(yè)面.loginPage('/login.html')// 登錄url.loginProcessingUrl('/auth/login').successHandler(myAuthenticationSuccessHandler).failureHandler(mySimpleUrlAuthenticationFailureHandler).and().authorizeRequests()// 該controller需要授權(quán).antMatchers('/user/auth').permitAll()// 添加一個(gè)url匹配器,如果匹配到login.html,就授權(quán).antMatchers('/login.html').permitAll().anyRequest().authenticated().and()// 關(guān)閉spring security默認(rèn)的防csrf攻擊.csrf().disable(); }
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持好吧啦網(wǎng)。
相關(guān)文章:
1. 基于PHP做個(gè)圖片防盜鏈2. ASP.NET MVC使用Boostrap實(shí)現(xiàn)產(chǎn)品展示、查詢、排序、分頁(yè)3. XML在語(yǔ)音合成中的應(yīng)用4. asp.net core 認(rèn)證和授權(quán)實(shí)例詳解5. .NET中實(shí)現(xiàn)對(duì)象數(shù)據(jù)映射示例詳解6. php使用正則驗(yàn)證密碼字段的復(fù)雜強(qiáng)度原理詳細(xì)講解 原創(chuàng)7. ASP.NET MVC把數(shù)據(jù)庫(kù)中枚舉項(xiàng)的數(shù)字轉(zhuǎn)換成文字8. 如何使用ASP.NET Core 配置文件9. jscript與vbscript 操作XML元素屬性的代碼10. 基于javaweb+jsp實(shí)現(xiàn)企業(yè)車輛管理系統(tǒng)
