SpringSecurity中的表單認(rèn)證詳細(xì)解析
一、默認(rèn)表單認(rèn)證:
首先,新建一個(gè) configuration 包用于存放通用配置;然后新建一個(gè) WebSecurityConfig 類,使其繼承 WebSecurityConfigurerAdapter ,如下所示:
@EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter{ }
在給WebSecurityConfig 類上加上 @EnableWebSecurity 注解后,便會(huì)自動(dòng)被 Spring 發(fā)現(xiàn)并注冊(cè)(點(diǎn)擊@EnableWebSecurity 注解可以看到 @Configuration 注解已經(jīng)存在,所以此處不需要額外添加)。
我們接著查看WebSecurityConfigurerAdapter 類對(duì) configure(HttpSecurity http)方法的定義。如下所示:
protected void configure(HttpSecurity http) throws Exception { logger.debug("Using default configure(HttpSecurity). If subclassed this will potentially override subclass configure(HttpSecurity)."); http .authorizeRequests() .anyRequest().authenticated() .and() .formLogin().and() .httpBasic(); }
可以看到WebSecurityConfigurerAdapter 已經(jīng)默認(rèn)聲明了一些安全特性:
a、驗(yàn)證所有用戶請(qǐng)求。
b、允許用戶使用表單登錄進(jìn)行身份驗(yàn)證(Spring Security 提供了一個(gè)簡(jiǎn)單的表單登錄頁(yè)面)。
c、允許用戶使用 HTTP 基本認(rèn)證。
現(xiàn)在重啟服務(wù),應(yīng)用新的安全配置??梢灶A(yù)見,在下次訪問(wèn) localhost:8080 時(shí),系統(tǒng)會(huì)要求我們進(jìn)行表單認(rèn)證。如下圖所示:
在上圖中我們可以發(fā)現(xiàn),我們?cè)L問(wèn)的地址自動(dòng)跳轉(zhuǎn)到 localhost:8080/login ,這正是 Spring Security 的默認(rèn)登錄頁(yè),只需要輸入正確的用戶名和密碼便可跳轉(zhuǎn)到回原來(lái)的訪問(wèn)地址。
二、自定義表單認(rèn)證
1、初步配置自定義表單登錄頁(yè)
雖然自動(dòng)生成的表單登錄頁(yè)可以方便、快速地啟動(dòng),但是大多數(shù)應(yīng)用程序更希望提供自己的表單登錄頁(yè),此時(shí)就需要覆蓋 configure() 方法,如下所示:
@EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter{ protected void configure(HttpSecurity http) throws Exception{ http.authorizeRequests() .anyRequest().authenticated() .and() .formLogin(). loginPage("/myLogin.html") // 使登錄頁(yè)不設(shè)限訪問(wèn) .permitAll() .and(). csrf().disable(); } }
2、認(rèn)識(shí) HttpSecurity
HttpSecurity 實(shí)際上對(duì)應(yīng)了 Spring Security 命名空間配置方式中 xml 文件內(nèi)的標(biāo)簽。允許我們?yōu)樘囟ǖ?http 請(qǐng)求配置安全策略。
在 xml 文件中,聲明大量配置早已司空見慣;但在 Java 配置中,按照傳統(tǒng)的方式,我們需要這樣來(lái)調(diào)用,如下所示:
@EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter{ protected void configure(HttpSecurity http) throws Exception{ ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry urlRegistry=http.authorizeRequests(); ExpressionUrlAuthorizationConfigurer.AuthorizedUrl authorizedUrl = (ExpressionUrlAuthorizationConfigurer.AuthorizedUrl)urlRegistry .anyRequest(); authorizedUrl.authenticated(); // more FormLoginConfigurer<HttpSecurity> formLoginConfigurer = http.formLogin(); formLoginConfigurer.loginPage("/myLogin.html"); formLoginConfigurer.permitAll(); // more } }
可以想象出這是多么煩瑣且令人痛苦的一件事。HttpSecurity 首先被設(shè)計(jì)為鏈?zhǔn)秸{(diào)用,在執(zhí)行每個(gè)方法后,都會(huì)返回一個(gè)預(yù)期的上下文,便于連續(xù)調(diào)用。我們不需要關(guān)心每個(gè)方法究竟返回了什么、如何進(jìn)行下一個(gè)配置等細(xì)節(jié)。
HttpSecurity 提供了很多配置相關(guān)的方法,分別對(duì)應(yīng)命名空間配置中的子標(biāo)簽 <http>。例如,authorizeRequests()、formLogin()、httpBasic() 和csrf() 分別對(duì)應(yīng) <intercept-url>、<form-login>、<http-basic> 和 <csrf> 標(biāo)簽。調(diào)用這些方法之后,除非使用 and() 方法結(jié)束當(dāng)前標(biāo)簽,上下文才會(huì)回到 HttpSecurity ,否則鏈?zhǔn)秸{(diào)用的上下文將自動(dòng)進(jìn)入對(duì)應(yīng)的標(biāo)簽域。
authorizeRequests() 方法實(shí)際上返回了一個(gè) URL 攔截注冊(cè)器,我們可以調(diào)用它提供的 anyRequest()、antMatchers() 和regexMatchers() 等方法來(lái)匹配系統(tǒng)的 URL ,并為其指定安全策略。
formLogin() 和httpBasic() 方法都聲明了需要 Spring Security 提供的表單認(rèn)證方式,分別返回對(duì)應(yīng)的配置器。其中,formLogin.loginPage("/myLogin.html") 指定自定義的登錄頁(yè)為/myLogin.html ,同時(shí),Spring Security 會(huì)用 /myLogin.html 注冊(cè)一個(gè) POST 路由,用于接收登錄請(qǐng)求。
csrf() 方法是 Spring Security 提供的跨站請(qǐng)求偽造防護(hù)功能,當(dāng)我們繼承WebSecurityConfigurerAdapter 會(huì)默認(rèn)開啟 csrf() 方法,關(guān)于 csrf() 方法的更多內(nèi)容會(huì)在后面的章節(jié)專門探討,以使測(cè)試進(jìn)程更加順利。
重新啟動(dòng)服務(wù)后在此訪問(wèn) localhost:8080 ,頁(yè)面會(huì)自動(dòng)跳轉(zhuǎn)到 localhost:8080/myLogin.html。由于 /myLogin.html 無(wú)法定位到頁(yè)面資源,所以會(huì)顯示一個(gè) 404 頁(yè)面,如下所示:
3、編寫表單登錄頁(yè)
表單登錄頁(yè)myLogin.html的代碼如下所示:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>登錄</title> </head> <body> <div class = "login" style="width:300px;height:300px"> <h2>Acced Form</h2> <div class ="login-top"></div> <h1>LOGIN FORM</h1> <form action="myLogin.html" method="post"> <input type="text" name="username" placeholder="username"/> <input type="password" name="password" placeholder="password"/> <div class="forgot" style="margin-top:20px;"> <a href="#">forgot Password</a> <input type="submit" value="login"> </div> </form> <div class="login-bottom"> <h3>New User <a href ="">Register</a> </h3> </div> </div> </body> </html>
在表單登錄頁(yè)中,僅有一個(gè)表單,用戶名和密碼分別為 username 和 password,并以 POST 的方式提交到 /myLogin.html。
我們將該文件放置在 resources/static/ 下。重啟服務(wù),再次訪問(wèn) localhost:8080,即可看到自定義的表單登錄頁(yè),如下所示:
輸入正確的用戶名和密碼后,單擊 login 按鈕,即可成功跳轉(zhuǎn)。
4、其他表單配置項(xiàng)
在自定義表單登錄頁(yè)之后,處理登錄請(qǐng)求的 URL 也會(huì)相應(yīng)改變,如何自定義 URL 呢?很簡(jiǎn)單, Spring Security 在表單定制里提供了相應(yīng)的支持,代碼如下所示:
@EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter{ protected void configure(HttpSecurity http) throws Exception{ http.authorizeRequests() .anyRequest().authenticated() .and() .formLogin() .loginPage("/myLogin.html") .loginProcessingUrl("/login") .permitAll() .and() .csrf().disable(); } }
此時(shí),有些讀者可能會(huì)有疑問(wèn),因?yàn)榘凑諔T例,在發(fā)送登錄請(qǐng)求并認(rèn)證成功之后,頁(yè)面會(huì)跳轉(zhuǎn)回原訪問(wèn)頁(yè)。在某些系統(tǒng)中的確是跳轉(zhuǎn)回原訪問(wèn)頁(yè)的,但在部分前后端完全分離、僅靠 json完成所有交互的系統(tǒng)中,一般會(huì)在登錄時(shí)返回一段 json數(shù)據(jù),告知前端成功登錄成功與否,由前端決定如何處理后續(xù)邏輯,而非由服務(wù)器主動(dòng)執(zhí)行頁(yè)面跳轉(zhuǎn)。這在 Spring Security 中同樣可以實(shí)現(xiàn):
@EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter{ protected void configure(HttpSecurity http) throws Exception{ http.authorizeRequests() .anyRequest().authenticated() .and() .formLogin() .loginPage("/myLogin.html") .loginProcessingUrl("/login") .successHandler(new AuthenticationSuccessHandler() { @Override public void onAuthenticationSuccess(HttpServletRequest arg0, HttpServletResponse arg1, Authentication arg2) throws IOException, ServletException { arg1.setContentType("application/json;charset=UTF-8"); PrintWriter out = arg1.getWriter(); out.write("{\"error_code\":\"0\",\"message\":\"歡迎登錄系統(tǒng)\"}"); } }) .failureHandler(new AuthenticationFailureHandler() { @Override public void onAuthenticationFailure(HttpServletRequest arg0, HttpServletResponse arg1, AuthenticationException arg2) throws IOException, ServletException { arg1.setContentType("application/json;charset=UTF-8"); arg1.setStatus(401); PrintWriter out = arg1.getWriter(); // 輸出失敗的原因 out.write("{\"error_code\":\"401\",\"name\":\""+arg2.getCause()+"\"," + "\"message\":\""+arg2.getMessage()+"\"}}"); } }) .and() .csrf().disable(); } }
表單登錄配置模塊提供了 successHandler() 和 failureHandler() 兩個(gè)方法,分別處理登錄成功和登錄失敗的邏輯。
其中,successHandler() 方法帶有一個(gè) Authentication 參數(shù),攜帶當(dāng)前登錄用戶名及其角色等信息;而 failureHandler() 方法攜帶一個(gè) AuthenticationException 異常參數(shù)。
具體處理方式需按照系統(tǒng)的情況自定義。
在形式上,我們確實(shí)使用了 Spring Security 的表單認(rèn)證功能,并且自定義了表單登錄頁(yè)。但實(shí)際上,這還遠(yuǎn)遠(yuǎn)不夠。
例如,在實(shí)際系統(tǒng)中,我們正常登錄時(shí)使用的用戶名和密碼都來(lái)自數(shù)據(jù)庫(kù),這里卻都寫在配置上。
更進(jìn)一步,我們可以對(duì)每個(gè)登錄用戶都設(shè)定詳細(xì)的權(quán)限,而并非一個(gè)通用角色。這些內(nèi)容將在后面章節(jié)講解。
到此這篇關(guān)于SpringSecurity中的表單認(rèn)證詳細(xì)解析的文章就介紹到這了,更多相關(guān)SpringSecurity表單認(rèn)證內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java Swing SpringLayout彈性布局的實(shí)現(xiàn)代碼
這篇文章主要介紹了Java Swing SpringLayout彈性布局的實(shí)現(xiàn)代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12Java動(dòng)態(tài)數(shù)組ArrayList實(shí)現(xiàn)動(dòng)態(tài)原理
ArrayList是一種動(dòng)態(tài)數(shù)組,它可以在運(yùn)行時(shí)自動(dòng)調(diào)整大小以適應(yīng)元素的添加和刪除,在Java中,你可以使用ArrayList類來(lái)實(shí)現(xiàn)動(dòng)態(tài)數(shù)組,本文將給大家介紹一下ArrayList動(dòng)態(tài)數(shù)組,是怎么實(shí)現(xiàn)動(dòng)態(tài)的2023-08-08String?concat(String?str)使用小結(jié)
這篇文章主要介紹了String?concat(String?str)使用小結(jié),在了解concat()之前,首先需要明確的是String的兩點(diǎn)特殊性,一是長(zhǎng)度不可變二是值不可變,本文給大家詳細(xì)講解,需要的朋友可以參考下2022-11-11淺析Spring容器原始Bean是如何創(chuàng)建的
這篇文章主要是想和小伙伴們一起聊聊?Spring?容器創(chuàng)建?Bean?最最核心的?createBeanInstance?方法,文中的示例代碼講解詳細(xì),需要的可以參考一下2023-08-08Java中Stringbuild,Date和Calendar類的用法詳解
這篇文章主要為大家詳細(xì)介紹了Java中Stringbuild、Date和Calendar類的用法,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起了解一下2023-04-04spring boot 實(shí)現(xiàn)阿里云視頻點(diǎn)播功能(刪除視頻)
這篇文章主要介紹了spring boot 實(shí)現(xiàn)阿里云視頻點(diǎn)播(刪除視頻功能),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12Nacos通過(guò)RefreshScope實(shí)現(xiàn)配置自動(dòng)更新的方式分享
這篇文章主要給大家介紹了Nacos如何通過(guò)RefreshScope實(shí)現(xiàn)配置自動(dòng)更新,文中給了兩種實(shí)現(xiàn)方式供大家參考,對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2023-09-09