SpringSecurity資源放行策略有哪些-創(chuàng)新互聯(lián)

本篇內(nèi)容主要講解“Spring Security資源放行策略有哪些”,感興趣的朋友不妨來看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“Spring Security資源放行策略有哪些”吧!

創(chuàng)新互聯(lián)主營(yíng)長(zhǎng)海網(wǎng)站建設(shè)的網(wǎng)絡(luò)公司,主營(yíng)網(wǎng)站建設(shè)方案,成都app開發(fā),長(zhǎng)海h5重慶小程序開發(fā)公司搭建,長(zhǎng)海網(wǎng)站營(yíng)銷推廣歡迎長(zhǎng)海等地區(qū)企業(yè)咨詢

Spring Security 中,到底該怎么樣給資源額外放行?

1.兩種思路

在 Spring Security 中,有一個(gè)資源,如果你希望用戶不用登錄就能訪問,那么一般來說,你有兩種配置策略:

第一種就是在 configure(WebSecurity web) 方法中配置放行,像下面這樣:

@Override
public void configure(WebSecurity web) throws Exception {
   web.ignoring().antMatchers("/css/**", "/js/**", "/index.html", "/img/**", "/fonts/**", "/favicon.ico", "/verifyCode");
}

第二種方式是在 configure(HttpSecurity http) 方法中進(jìn)行配置:

http.authorizeRequests()
       .antMatchers("/hello").permitAll()
       .anyRequest().authenticated()

兩種方式大的區(qū)別在于,第一種方式是不走 Spring Security 過濾器鏈,而第二種方式走 Spring Security 過濾器鏈,在過濾器鏈中,給請(qǐng)求放行。

在我們使用 Spring Security 的時(shí)候,有的資源可以使用第一種方式額外放行,不需要驗(yàn)證,例如前端頁(yè)面的靜態(tài)資源,就可以按照第一種方式配置放行。

有的資源放行,則必須使用第二種方式,例如登錄接口。大家知道,登錄接口也是必須要暴露出來的,不需要登錄就能訪問到的,但是我們卻不能將登錄接口用第一種方式暴露出來,登錄請(qǐng)求必須要走 Spring Security 過濾器鏈,因?yàn)樵谶@個(gè)過程中,還有其他事情要做。

接下來我以登錄接口為例,來和小伙伴們分析一下走 Spring Security 過濾器鏈有什么不同。

2.登錄請(qǐng)求分析

首先大家知道,當(dāng)我們使用 Spring Security,用戶登錄成功之后,有兩種方式獲取用戶登錄信息:

  1. SecurityContextHolder.getContext().getAuthentication()

  2. 在 Controller 的方法中,加入 Authentication 參數(shù)

這兩種辦法,都可以獲取到當(dāng)前登錄用戶信息。

這兩種方式獲取到的數(shù)據(jù)都是來自 SecurityContextHolder,SecurityContextHolder 中的數(shù)據(jù),本質(zhì)上是保存在 ThreadLocal 中,ThreadLocal 的特點(diǎn)是存在它里邊的數(shù)據(jù),哪個(gè)線程存的,哪個(gè)線程才能訪問到。

這樣就帶來一個(gè)問題,當(dāng)用戶登錄成功之后,將用戶用戶數(shù)據(jù)存在 SecurityContextHolder 中(thread1),當(dāng)下一個(gè)請(qǐng)求來的時(shí)候(thread2),想從 SecurityContextHolder 中獲取用戶登錄信息,卻發(fā)現(xiàn)獲取不到!為啥?因?yàn)樗鼈z不是同一個(gè) Thread。

但實(shí)際上,正常情況下,我們使用 Spring Security 登錄成功后,以后每次都能夠獲取到登錄用戶信息,這又是怎么回事呢?

這我們就要引入 Spring Security 中的 
SecurityContextPersistenceFilter 了。

小伙伴們都知道,無論是 Spring Security 還是 Shiro,它的一系列功能其實(shí)都是由過濾器來完成的,在 Spring Security 中,前面跟大家聊了 
UsernamePasswordAuthenticationFilter 過濾器,在這個(gè)過濾器之前,還有一個(gè)過濾器就是
SecurityContextPersistenceFilter,請(qǐng)求在到達(dá) 
UsernamePasswordAuthenticationFilter 之前都會(huì)先經(jīng)過 
SecurityContextPersistenceFilter。

我們來看下它的源碼(部分):

public class SecurityContextPersistenceFilter extends GenericFilterBean {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request,
response);
SecurityContext contextBeforeChainExecution = repo.loadContext(holder);
try {
SecurityContextHolder.setContext(contextBeforeChainExecution);
chain.doFilter(holder.getRequest(), holder.getResponse());
}
finally {
SecurityContext contextAfterChainExecution = SecurityContextHolder
.getContext();
SecurityContextHolder.clearContext();
repo.saveContext(contextAfterChainExecution, holder.getRequest(),
holder.getResponse());
}
}
}

原本的方法很長(zhǎng),我這里列出來了比較關(guān)鍵的幾個(gè)部分:

  1. SecurityContextPersistenceFilter 繼承自 GenericFilterBean,而 GenericFilterBean 則是 Filter 的實(shí)現(xiàn),所以 SecurityContextPersistenceFilter 作為一個(gè)過濾器,它里邊最重要的方法就是 doFilter 了。

  2. 在 doFilter 方法中,它首先會(huì)從 repo 中讀取一個(gè) SecurityContext 出來,這里的 repo 實(shí)際上就是 HttpSessionSecurityContextRepository,讀取 SecurityContext 的操作會(huì)進(jìn)入到 readSecurityContextFromSession 方法中,在這里我們看到了讀取的核心方法 Object contextFromSession = httpSession.getAttribute(springSecurityContextKey);,這里的 springSecurityContextKey 對(duì)象的值就是 SPRING_SECURITY_CONTEXT,讀取出來的對(duì)象最終會(huì)被轉(zhuǎn)為一個(gè) SecurityContext 對(duì)象。

  3. SecurityContext 是一個(gè)接口,它有一個(gè)唯一的實(shí)現(xiàn)類 SecurityContextImpl,這個(gè)實(shí)現(xiàn)類其實(shí)就是用戶信息在 session 中保存的 value。

  4. 在拿到 SecurityContext 之后,通過 SecurityContextHolder.setContext 方法將這個(gè) SecurityContext 設(shè)置到 ThreadLocal 中去,這樣,在當(dāng)前請(qǐng)求中,Spring Security 的后續(xù)操作,我們都可以直接從 SecurityContextHolder 中獲取到用戶信息了。

  5. 接下來,通過 chain.doFilter 讓請(qǐng)求繼續(xù)向下走(這個(gè)時(shí)候就會(huì)進(jìn)入到 UsernamePasswordAuthenticationFilter 過濾器中了)。

  6. 在過濾器鏈走完之后,數(shù)據(jù)響應(yīng)給前端之后,finally 中還有一步收尾操作,這一步很關(guān)鍵。這里從 SecurityContextHolder 中獲取到 SecurityContext,獲取到之后,會(huì)把 SecurityContextHolder 清空,然后調(diào)用 repo.saveContext 方法將獲取到的 SecurityContext 存入 session 中。

至此,整個(gè)流程就很明了了。

每一個(gè)請(qǐng)求到達(dá)服務(wù)端的時(shí)候,首先從 session 中找出來 SecurityContext ,然后設(shè)置到 SecurityContextHolder 中去,方便后續(xù)使用,當(dāng)這個(gè)請(qǐng)求離開的時(shí)候,SecurityContextHolder 會(huì)被清空,SecurityContext 會(huì)被放回 session 中,方便下一個(gè)請(qǐng)求來的時(shí)候獲取。

登錄請(qǐng)求來的時(shí)候,還沒有登錄用戶數(shù)據(jù),但是登錄請(qǐng)求走的時(shí)候,會(huì)將用戶登錄數(shù)據(jù)存入 session 中,下個(gè)請(qǐng)求到來的時(shí)候,就可以直接取出來用了。

看了上面的分析,我們可以至少得出兩點(diǎn)結(jié)論:

  1. 如果我們暴露登錄接口的時(shí)候,使用了前面提到的第一種方式,沒有走 Spring Security,過濾器鏈,則在登錄成功后,就不會(huì)將登錄用戶信息存入 session 中,進(jìn)而導(dǎo)致后來的請(qǐng)求都無法獲取到登錄用戶信息(后來的請(qǐng)求在系統(tǒng)眼里也都是未認(rèn)證的請(qǐng)求)

  2. 如果你的登錄請(qǐng)求正常,走了 Spring Security 過濾器鏈,但是后來的 A 請(qǐng)求沒走過濾器鏈(采用前面提到的第一種方式放行),那么 A 請(qǐng)求中,也是無法通過 SecurityContextHolder 獲取到登錄用戶信息的,因?yàn)樗婚_始沒經(jīng)過 SecurityContextPersistenceFilter 過濾器鏈。

3.總結(jié)

總之,前端靜態(tài)資源放行時(shí),可以直接不走 Spring Security 過濾器鏈,像下面這樣:

@Override
public void configure(WebSecurity web) throws Exception {
   web.ignoring().antMatchers("/css/**","/js/**","/index.html","/img/**","/fonts/**","/favicon.ico");
}

后端的接口要額外放行,就需要仔細(xì)考慮場(chǎng)景了,不過一般來說,不建議使用上面這種方式,建議下面這種方式,原因前面已經(jīng)說過了:

http.authorizeRequests()
       .antMatchers("/hello").permitAll()
       .anyRequest().authenticated()

到此,相信大家對(duì)“Spring Security資源放行策略有哪些”有了更深的了解,不妨來實(shí)際操作一番吧!這里是創(chuàng)新互聯(lián)網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

本文名稱:SpringSecurity資源放行策略有哪些-創(chuàng)新互聯(lián)
文章來源:http://muchs.cn/article0/cdopoo.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供云服務(wù)器、自適應(yīng)網(wǎng)站定制開發(fā)、網(wǎng)站策劃、網(wǎng)站排名網(wǎng)頁(yè)設(shè)計(jì)公司

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

營(yíng)銷型網(wǎng)站建設(shè)