SpringSecurity表單配置之登錄成功及頁面跳轉(zhuǎn)原理解析
登錄表單配置
在上一篇文章中,我們介紹了,基本認(rèn)證以及默認(rèn)用戶名和密碼以及頁面SpringSecurity是怎樣幫我們生成的,這里我們就來看一下登錄表單的詳細(xì)配置。
項目準(zhǔn)備
導(dǎo)入依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>編寫登錄頁面(login.html)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>登錄</title>
<link rel="external nofollow" rel="stylesheet"
id="bootstrap-css"
/>
<script src="http://maxcdn.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js">
</script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js">
</script>
</head>
<style>
</style>
<body>
<div id="login">
<div class="container">
<div id="login-row" class="row justify-content-center align-item-center">
<div id="login-colum" class="col-md-6">
<div id="login-box" class="col-md-12">
<form id="login-form" class="form" action="/doLogin" method="post">
<h3 class="text-center text-info">登錄</h3>
<div class="form-group">
<label for="username" class="text-info">用戶名:</label><br>
<input type="text" name="uname" id="username" class="form-control">
</div>
<div class="form-group">
<label for="password" class="text-info">密碼:</label><br>
<input type="text" name="passwd" id="password" class="form-control">
</div>
<div class="form-group">
<input type="submit" name="submit" class="btn btn-info btn-md" value="登錄">
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</body>
</html>提供兩個測試接口
@GetMapping("/hello")
public String hello(){
return "hello springboot security";
}
@GetMapping("/index")
public String index(){
return "login success";
}自定義用戶名密碼
spring.security.user.name=test spring.security.user.password=123456 spring.security.user.roles=admin,user
提供SpringSecurity配置類
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.and().formLogin()
.loginPage("/login.html")
.loginProcessingUrl("/doLogin")
.defaultSuccessUrl("/index")
.failureUrl("/login.html")
.usernameParameter("uname")
.passwordParameter("passwd")
.permitAll()
.and()
.csrf().disable();
}
}在SpringSecurity中,如果我們需要自定義配置,基本上都是繼承WebSecurityConfigurerAdapter來實現(xiàn)的,當(dāng)然WebSecurityConfigurerAdapter本身的配置還是比較復(fù)雜,同時也是比較豐富的,這里不細(xì)說,后續(xù)會詳細(xì)介紹。
- 首先configure方法中是一個鏈?zhǔn)脚渲茫?dāng)然也可以不用鏈?zhǔn)脚渲茫總€屬性配置完畢后再從http重新寫起
- authorizeRequests()方法表示開啟權(quán)限配置
- anyRequest().authenticated()表示所有的請求都要認(rèn)證之后才能訪問
- and()方法,該方法會返回一個HttpSecurityBuilder對象的一個子類(實際上就算HttpSecurity),所以and()方法相當(dāng)于又回到HttpSecurity實例,重新開啟新一輪的配置。
- formLogin()表示開啟表單登錄配置:
- loginPage:用來配置登錄頁面地址
- loginProcessingUrl:用來配置接口登錄接口地址
- defaultSuccessUrl:表示登錄成功后跳轉(zhuǎn)地址
- failureUrl:表示登錄失敗后跳轉(zhuǎn)的地址
- usernameParameter:表示登錄用戶名的參數(shù)名稱
- passwordParameter:表示密碼的參數(shù)名稱
- permitAll:可以理解成兩個and()之間的所有方法地址不需要認(rèn)證攔截(白名單)。
需要注意的是loginProcessingUrl、usernameParameterpasswordParameter需要和login.html中登錄表單配置一致
最后csrf().disable()表示禁用CSRF防御功能,SpringSecurity自帶了CSRF防御機(jī)制,但是我們這里為了測試方便,先將CSRF防御機(jī)制關(guān)閉。
啟動項目訪問http://localhost:8080/index

輸入配置的用戶名/密碼:test/123456,然后就能訪問到/index接口了
配置細(xì)節(jié)
上面我們說到defaultSuccessUrl和failureUrl表示用戶登錄失敗后的跳轉(zhuǎn)地址。關(guān)于登錄成功和登錄失敗,除了這兩個方法之外,還有另外兩個方法可以配置
登錄成功
- successForwardUrl
- defaultSuccessUrl
defaultSuccessUrl前者表示當(dāng)用戶登錄成功之后,會自動重定向到登錄之前的地址上,如果用戶本身就是直接訪問的登錄頁面,則登錄成功之后就會重定向到defaultSuccessUrl指定的頁面中。例如用戶在未認(rèn)證的情況下,訪問了/hello頁面,此時會重定向到登錄頁面,登錄成功后,就會自動重定向到/hello頁面;而用戶如果一開始就是訪問到登錄頁面,則登錄成功后就會自動重定向到defaultSuccessUrl指定的頁面
successForwardUrl則不會考慮用戶之前所訪問地址,只要用戶登錄成功,就會通過服務(wù)器端跳轉(zhuǎn)到successForwardUrl所指定的頁面。
defaultSuccessUrl有一個重載方法,如果重載方法的第二個參數(shù)傳入true,則它和successForwardUrl效果類似,即不考慮用戶之前的訪問地址,只要登錄成功就重定向到指定頁面。不同之處在于defaultSuccessUrl是通過重定向?qū)崿F(xiàn)的跳轉(zhuǎn)(客戶端跳轉(zhuǎn)),而successForwardUrl是通過服務(wù)器端跳轉(zhuǎn)實現(xiàn)的。
無論是successForwardUrl還是defaultSuccessUrl,最終所有配置的都是AuthenticationSuccessHandler接口的實例。
SpringSecurity中專門提供了AuthenticationSuccessHandler接口用來處理登錄成功事項
public interface AuthenticationSuccessHandler {
default void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) throws IOException, ServletException {
this.onAuthenticationSuccess(request, response, authentication);
chain.doFilter(request, response);
}
void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException;
}由上述代碼可以看到AuthenticationSuccessHandler接口中一共定義了兩個方法,一個是default方法,此方法是SpringSecurity5.2開始加入的,在處理特定的認(rèn)證請求Authentication Filter中會用到;另外一個非default方法,則用來處理登錄成功的具體事項,其中authentication參數(shù)保存了登錄成功的用戶信息。
AuthenticationSuccessHandler接口一共有三個實現(xiàn)類:

(1)、SimpleUrlAuthenticationSuccessHandler繼承自AbstractAuthenticationTargetUrlRequestHandler,通過AbstractAuthenticationTargetUrlRequestHandler中的handle方法實現(xiàn)請求重定向
(2)、SavedRequestAwareAuthenticationSuccessHandler在SimpleUrlAuthenticationSuccessHandler的基礎(chǔ)之上增加了請求緩存的功能,可以記錄之前請求的地址,進(jìn)而在登錄成功之后重定向到一開始訪問的地址。
(3)、ForwardAuthenticationSuccessHandler的實現(xiàn)就比較容易,就是一個服務(wù)端跳轉(zhuǎn)。
我們來重點看一下SavedRequestAwareAuthenticationSuccessHandler和ForwardAuthenticationSuccessHandler
public class SavedRequestAwareAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
protected final Log logger = LogFactory.getLog(this.getClass());
private RequestCache requestCache = new HttpSessionRequestCache();
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws ServletException, IOException {
SavedRequest savedRequest = this.requestCache.getRequest(request, response);
if (savedRequest == null) {
super.onAuthenticationSuccess(request, response, authentication);
return;
}
String targetUrlParameter = getTargetUrlParameter();
if (isAlwaysUseDefaultTargetUrl()
|| (targetUrlParameter != null && StringUtils.hasText(request.getParameter(targetUrlParameter)))) {
this.requestCache.removeRequest(request, response);
super.onAuthenticationSuccess(request, response, authentication);
return;
}
clearAuthenticationAttributes(request);
// Use the DefaultSavedRequest URL
String targetUrl = savedRequest.getRedirectUrl();
getRedirectStrategy().sendRedirect(request, response, targetUrl);
}
public void setRequestCache(RequestCache requestCache) {
this.requestCache = requestCache;
}
}這里的核心方法就是 onAuthenticationSuccess
(1)、首先從requestCache中獲取緩存下來的請求,如果沒有獲取到緩存請求,就說明用戶在登錄頁面之前并沒有訪問其他頁面,此時調(diào)用父類的方法來處理,最終會重定向到defaultSuccessUrl指定的地址
(2)、如果緩存請求不為空,則會獲取一個targetUrlParameter,這個是用戶顯示指定的,希望登錄成功重定向的地址,例如用戶發(fā)送的登錄請求是http://localhost:8080/doLogin?target=/hello,這就表示當(dāng)用戶登錄成功之后。希望自動重定向到/hello這個接口,getTargetUrlParameter就是要獲取重定向地址參數(shù)的key,也就是上面的target,拿到target之后,就可以獲取到重定向地址了。
(3)、如果targetUrlParameter存在,或者用戶設(shè)置了alwaysUseDefaultTargetUrl為true,這個時候緩存下來的請求就沒有意義了。此時會直接調(diào)用父類的onAuthenticationSuccess方法完成重定向。targetUrlParameter存在,則直接重定向到targetUrlParameter指定的地址。alwaysUseDefaultTargetUrl為true,則直接重定向到defaultSuccessUrl指定的地址。如果alwaysUseDefaultTargetUrl和targetUrlParameter同時滿足,則重定向到defaultSuccessUrl指定的地址。
(4)、如果前面的條件都不滿足,那么最終會從緩存請求saveRequest中獲取重定向地址,然后進(jìn)行重定向操作。
這就是SavedRequestAwareAuthenticationSuccessHandler的實現(xiàn)邏輯,開發(fā)者也可以配置自己的SavedRequestAwareAuthenticationSuccessHandler,代碼如下:
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.and().formLogin()
.loginPage("/login.html")
.loginProcessingUrl("/doLogin")
.successHandler(successHandler())
// .defaultSuccessUrl("/index")
.failureUrl("/login.html")
.usernameParameter("uname")
.passwordParameter("passwd")
.permitAll()
.and()
.csrf().disable();
}
SavedRequestAwareAuthenticationSuccessHandler successHandler(){
SavedRequestAwareAuthenticationSuccessHandler handler = new SavedRequestAwareAuthenticationSuccessHandler();
handler.setDefaultTargetUrl("/hello");
handler.setTargetUrlParameter("target");
return handler;
}
}然后在上篇文章中的表單中,修改一下action的參數(shù)
action="/doLogin?target=http://www.baidu.com"
這樣當(dāng)我們登錄成功之后就可以跳轉(zhuǎn)到百度了,如果不指定action的target就會跳轉(zhuǎn)到我們上面默認(rèn)的/hello接口
當(dāng)我們通過successForwardUrl來設(shè)置登錄成功后重定向地址時,實際上對應(yīng)的實現(xiàn)類就是ForwardAuthenticationSuccessHandler,它的源碼特別簡單,就是一個服務(wù)端轉(zhuǎn)發(fā),如下:
public class ForwardAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
private final String forwardUrl;
/**
* @param forwardUrl
*/
public ForwardAuthenticationSuccessHandler(String forwardUrl) {
Assert.isTrue(UrlUtils.isValidRedirectUrl(forwardUrl), () -> "'" + forwardUrl + "' is not a valid forward URL");
this.forwardUrl = forwardUrl;
}
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
request.getRequestDispatcher(this.forwardUrl).forward(request, response);
}
}上述代碼可以看到,主要共嗯那個就是調(diào)用getRequestDispatcher方法進(jìn)行服務(wù)端轉(zhuǎn)發(fā),AuthenticationSuccessHandler默認(rèn)的三個實現(xiàn)類,無論是哪一個,都是用來處理頁面跳轉(zhuǎn)的。有時候頁面跳轉(zhuǎn)并不能滿足我們的需求,特別是現(xiàn)在的前后的分離開發(fā)中,用戶登錄成功之后,就不需要跳轉(zhuǎn)頁面了,只需要給前端返回一個JSON數(shù)據(jù)即可,告訴前端登錄成功還是失敗,前端收到消息后自行處理,像這樣的需求,我們可以通過自定義AuthenticationSuccessHandler的實現(xiàn)類來完成,如下:
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
response.setContentType("application/json;charset=utf-8");
HashMap<String,Object> resp = new HashMap<>();
resp.put("status",200);
resp.put("msg","登錄成功");
ObjectMapper om = new ObjectMapper();
final String writeValueAsString = om.writeValueAsString(resp);
response.getWriter().write(writeValueAsString);
}
}然后在WebSecurity中配置
http.successHandler(new MyAuthenticationSuccessHandler());
配置完成之后,此時登錄成功,就不會進(jìn)行頁面添磚了,而是返回一段JSON字符串。
關(guān)于登錄失敗的,會在下一篇中詳細(xì)講解。
到此這篇關(guān)于SpringSecurity表單配置之登錄成功及頁面跳轉(zhuǎn)原理的文章就介紹到這了,更多相關(guān)SpringSecurity登錄成功跳轉(zhuǎn)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringBoot+SpringSecurity處理Ajax登錄請求問題(推薦)
- 解析SpringSecurity自定義登錄驗證成功與失敗的結(jié)果處理問題
- SpringSecurity動態(tài)加載用戶角色權(quán)限實現(xiàn)登錄及鑒權(quán)功能
- 解決SpringSecurity 一直登錄失敗的問題
- SpringSecurity多表多端賬戶登錄的實現(xiàn)
- SpringBoot如何整合Springsecurity實現(xiàn)數(shù)據(jù)庫登錄及權(quán)限控制
- SpringSecurity6.x多種登錄方式配置小結(jié)
- SpringSecurity集成第三方登錄過程詳解(最新推薦)
- Spring?Security重寫AuthenticationManager實現(xiàn)賬號密碼登錄或者手機(jī)號碼登錄
相關(guān)文章
Java的synchronized關(guān)鍵字深入解析
這篇文章主要介紹了Java的synchronized關(guān)鍵字深入解析,在并發(fā)編程中,多線程同時并發(fā)訪問的資源叫做臨界資源,當(dāng)多個線程同時訪問對象并要求操作相同資源時,分割了原子操作就有可能出現(xiàn)數(shù)據(jù)的不一致或數(shù)據(jù)不完整的情況,需要的朋友可以參考下2023-12-12
Spring WebFlux使用函數(shù)式編程模型構(gòu)建異步非阻塞服務(wù)
這篇文章主要介紹了Spring WebFlux使用函數(shù)式編程模型構(gòu)建異步非阻塞服務(wù),重點介紹如何使用函數(shù)式編程模型創(chuàng)建響應(yīng)式 RESTful 服務(wù),這種編程模型與傳統(tǒng)的基于 Spring MVC 構(gòu)建 RESTful 服務(wù)的方法有較大差別,感興趣的朋友跟隨小編一起看看吧2023-08-08
高級數(shù)據(jù)結(jié)構(gòu)及應(yīng)用之使用bitmap進(jìn)行字符串去重的方法實例
今天小編就為大家分享一篇關(guān)于高級數(shù)據(jù)結(jié)構(gòu)及應(yīng)用之使用bitmap進(jìn)行字符串去重的方法實例,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2019-02-02
mybatis-plus配置控制臺打印完整帶參數(shù)SQL語句的實現(xiàn)
這篇文章主要介紹了mybatis-plus配置控制臺打印完整帶參數(shù)SQL語句,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08

