怎么利用spring-security解決CSRF問(wèn)題

這篇文章主要介紹了怎么利用spring-security解決CSRF問(wèn)題,具有一定借鑒價(jià)值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。

創(chuàng)新互聯(lián)建站專業(yè)為企業(yè)提供龍圩網(wǎng)站建設(shè)、龍圩做網(wǎng)站、龍圩網(wǎng)站設(shè)計(jì)、龍圩網(wǎng)站制作等企業(yè)網(wǎng)站建設(shè)、網(wǎng)頁(yè)設(shè)計(jì)與制作、龍圩企業(yè)網(wǎng)站模板建站服務(wù),10余年龍圩做網(wǎng)站經(jīng)驗(yàn),不只是建網(wǎng)站,更提供有價(jià)值的思路和整體網(wǎng)絡(luò)服務(wù)。

CSRF介紹

CSRF(Cross-site request forgery),中文名稱:跨站請(qǐng)求偽造,也被稱為:one click attack/session riding,縮寫(xiě)為:CSRF/XSRF。

配置步驟

1.依賴jar包

<properties> 
    <spring.security.version>4.2.2.RELEASE</spring.security.version> 
  </properties> 
<dependency> 
        <groupId>org.springframework.security</groupId> 
        <artifactId>spring-security-core</artifactId> 
        <version>${spring.security.version}</version> 
      </dependency> 
 
      <dependency> 
        <groupId>org.springframework.security</groupId> 
        <artifactId>spring-security-web</artifactId> 
        <version>${spring.security.version}</version> 
      </dependency> 
 
      <dependency> 
        <groupId>org.springframework.security</groupId> 
        <artifactId>spring-security-config</artifactId> 
        <version>${spring.security.version}</version> 
      </dependency>

2.web.xml配置

<filter> 
    <filter-name>springSecurityFilterChain</filter-name> 
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> 
  </filter> 
 
  <filter-mapping> 
    <filter-name>springSecurityFilterChain</filter-name> 
    <url-pattern>/*</url-pattern> 
  </filter-mapping>

3.Spring配置文件配置

<bean id="csrfSecurityRequestMatcher" class="com.xxx.CsrfSecurityRequestMatcher"></bean> 
 
  <security:http auto-config="true" use-expressions="true"> 
    <security:headers> 
      <security:frame-options disabled="true"/> 
    </security:headers> 
    <security:csrf request-matcher-ref="csrfSecurityRequestMatcher" /> 
  </security:http>

4.自定義RequestMatcher的實(shí)現(xiàn)類CsrfSecurityRequestMatcher

這個(gè)類被用來(lái)自定義哪些請(qǐng)求是不需要進(jìn)行攔截過(guò)濾的。如果配置csrf,所有http請(qǐng)求都被會(huì)CsrfFilter攔截,而CsrfFilter中有一個(gè)私有類DefaultRequiresCsrfMatcher。

源碼1:DefaultRequiresCsrfMatcher類

private static final class DefaultRequiresCsrfMatcher implements RequestMatcher { 
    private final HashSet<String> allowedMethods; 
 
    private DefaultRequiresCsrfMatcher() { 
      this.allowedMethods = new HashSet(Arrays.asList(new String[]{"GET", "HEAD", "TRACE", "OPTIONS"})); 
    } 
 
    public boolean matches(HttpServletRequest request) { 
      return !this.allowedMethods.contains(request.getMethod()); 
    } 
  }

從這段源碼可以發(fā)現(xiàn),POST方法被排除在外了,也就是說(shuō)只有GET|HEAD|TRACE|OPTIONS這4類方法會(huì)被放行,其它Method的http請(qǐng)求,都要驗(yàn)證_csrf的token是否正確,而通常post方式調(diào)用rest接口服務(wù)時(shí),又沒(méi)有_csrf的token,所以會(huì)導(dǎo)致我們的rest接口調(diào)用失敗,我們需要自定義一個(gè)類對(duì)該類型接口進(jìn)行放行。來(lái)看下我們自定義的過(guò)濾器:

源碼2:csrfSecurityRequestMatcher類

public class CsrfSecurityRequestMatcher implements RequestMatcher { 
  private Pattern allowedMethods = Pattern.compile("^(GET|HEAD|TRACE|OPTIONS)$"); 
  private RegexRequestMatcher unprotectedMatcher = new RegexRequestMatcher("^/rest/.*", null); 
 
  @Override 
  public boolean matches(HttpServletRequest request) { 
    if(allowedMethods.matcher(request.getMethod()).matches()){ 
      return false; 
    } 
 
    return !unprotectedMatcher.matches(request); 
  } 
}

說(shuō)明:一般我們定義的rest接口服務(wù),都帶上 /rest/ ,所以如果你的項(xiàng)目中不是使用的這種,或者項(xiàng)目中沒(méi)有rest服務(wù),這個(gè)類完全可以省略的。

5.post請(qǐng)求配置

一般我們的項(xiàng)目中都有一個(gè)通用的jsp文件,就是每個(gè)頁(yè)面都會(huì)引用的,所以我們可以在通用文件中做如下配置:

<meta name="_csrf" content="${_csrf.token}"/> 
<meta name="_csrf_header" content="${_csrf.headerName}"/> 
 
<script> 
 
  var token = $("meta[name='_csrf']").attr("content"); 
  var header = $("meta[name='_csrf_header']").attr("content"); 
  $.ajaxSetup({ 
    beforeSend: function (xhr) { 
      if(header && token ){ 
        xhr.setRequestHeader(header, token); 
      } 
    }} 
  ); 
</script>

$.ajaxSetup的意思就是給我們所有的請(qǐng)求都加上這個(gè)header和token,或者放到form表單中。注意,_csrf這個(gè)要與spring security的配置文件中的配置相匹配,默認(rèn)為_(kāi)csrf。

源碼解析

我們知道,既然配置了csrf,所有的http請(qǐng)求都會(huì)被CsrfFilter攔截到,所以看下CsrfFilter的源碼就對(duì)原理一目了然了。這里我們只看具體過(guò)濾的方法即可:

源碼3:CsrfFilter的doFilterInternal方法

protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { 
    request.setAttribute(HttpServletResponse.class.getName(), response); 
    CsrfToken csrfToken = this.tokenRepository.loadToken(request); 
    boolean missingToken = csrfToken == null; 
    if(missingToken) {//如果token為空,說(shuō)明第一次訪問(wèn),生成一個(gè)token對(duì)象 
      csrfToken = this.tokenRepository.generateToken(request); 
      this.tokenRepository.saveToken(csrfToken, request, response); 
    } 
 
    request.setAttribute(CsrfToken.class.getName(), csrfToken); 
    //把token對(duì)象放到request中,注意這里key是csrfToken.getParameterName()= _csrf,所以我們頁(yè)面上才那么寫(xiě)死。 
    request.setAttribute(csrfToken.getParameterName(), csrfToken); 
     
    //這個(gè)macher就是我們?cè)赟pring配置文件中自定義的過(guò)濾器,也就是GET,HEAD, TRACE, OPTIONS和我們的rest都不處理 
    if(!this.requireCsrfProtectionMatcher.matches(request)) { 
      filterChain.doFilter(request, response); 
    } else { 
      String actualToken = request.getHeader(csrfToken.getHeaderName()); 
      if(actualToken == null) { 
        actualToken = request.getParameter(csrfToken.getParameterName()); 
      } 
 
      if(!csrfToken.getToken().equals(actualToken)) { 
        if(this.logger.isDebugEnabled()) { 
          this.logger.debug("Invalid CSRF token found for " + UrlUtils.buildFullRequestUrl(request)); 
        } 
 
        if(missingToken) { 
          this.accessDeniedHandler.handle(request, response, new MissingCsrfTokenException(actualToken)); 
        } else { 
          this.accessDeniedHandler.handle(request, response, new InvalidCsrfTokenException(csrfToken, actualToken)); 
        } 
 
      } else { 
        filterChain.doFilter(request, response); 
      } 
    } 
  }

從源碼中可以看到,通過(guò)我們自定義的過(guò)濾器以外的post請(qǐng)求都需要進(jìn)行token驗(yàn)證。

本來(lái)呢,是想截圖弄個(gè)案例上去的,然后通過(guò)斷點(diǎn)看看頁(yè)面和后臺(tái)的傳值情況....不過(guò),我這里沒(méi)法上傳圖片抓狂。好吧,就總結(jié)這么多吧!如果有寫(xiě)的不對(duì)的或者有其他問(wèn)題可以留言交流。

感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享的“怎么利用spring-security解決CSRF問(wèn)題”這篇文章對(duì)大家有幫助,同時(shí)也希望大家多多支持創(chuàng)新互聯(lián),關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,更多相關(guān)知識(shí)等著你來(lái)學(xué)習(xí)!

文章標(biāo)題:怎么利用spring-security解決CSRF問(wèn)題
標(biāo)題網(wǎng)址:http://muchs.cn/article42/jojeec.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供標(biāo)簽優(yōu)化、手機(jī)網(wǎng)站建設(shè)、虛擬主機(jī)微信小程序、服務(wù)器托管外貿(mào)網(wǎng)站建設(shè)

廣告

聲明:本網(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í)需注明來(lái)源: 創(chuàng)新互聯(lián)

綿陽(yáng)服務(wù)器托管