詳解利用spring-security解決CSRF問題
CSRF介紹
CSRF(Cross-site request forgery),中文名稱:跨站請求偽造,也被稱為:one click attack/session riding,縮寫為:CSRF/XSRF。
具體SCRF的介紹和攻擊方式請參看百度百科的介紹和一位大牛的分析:
CSRF百度百科
淺談CSRF攻擊方式
配置步驟
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的實現(xiàn)類CsrfSecurityRequestMatcher
這個類被用來自定義哪些請求是不需要進行攔截過濾的。如果配置csrf,所有http請求都被會CsrfFilter攔截,而CsrfFilter中有一個私有類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方法被排除在外了,也就是說只有GET|HEAD|TRACE|OPTIONS這4類方法會被放行,其它Method的http請求,都要驗證_csrf的token是否正確,而通常post方式調(diào)用rest接口服務(wù)時,又沒有_csrf的token,所以會導(dǎo)致我們的rest接口調(diào)用失敗,我們需要自定義一個類對該類型接口進行放行。來看下我們自定義的過濾器:
源碼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);
}
}
說明:一般我們定義的rest接口服務(wù),都帶上 /rest/ ,所以如果你的項目中不是使用的這種,或者項目中沒有rest服務(wù),這個類完全可以省略的。
5.post請求配置
一般我們的項目中都有一個通用的jsp文件,就是每個頁面都會引用的,所以我們可以在通用文件中做如下配置:
<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的意思就是給我們所有的請求都加上這個header和token,或者放到form表單中。注意,_csrf這個要與spring security的配置文件中的配置相匹配,默認為_csrf。
源碼解析
我們知道,既然配置了csrf,所有的http請求都會被CsrfFilter攔截到,所以看下CsrfFilter的源碼就對原理一目了然了。這里我們只看具體過濾的方法即可:
源碼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為空,說明第一次訪問,生成一個token對象
csrfToken = this.tokenRepository.generateToken(request);
this.tokenRepository.saveToken(csrfToken, request, response);
}
request.setAttribute(CsrfToken.class.getName(), csrfToken);
//把token對象放到request中,注意這里key是csrfToken.getParameterName()= _csrf,所以我們頁面上才那么寫死。
request.setAttribute(csrfToken.getParameterName(), csrfToken);
//這個macher就是我們在Spring配置文件中自定義的過濾器,也就是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);
}
}
}
從源碼中可以看到,通過我們自定義的過濾器以外的post請求都需要進行token驗證。
本來呢,是想截圖弄個案例上去的,然后通過斷點看看頁面和后臺的傳值情況....不過,我這里沒法上傳圖片抓狂。好吧,就總結(jié)這么多吧!如果有寫的不對的或者有其他問題可以留言交流。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
java,android,MD5加密算法的實現(xiàn)代碼(16位,32位)
下面小編就為大家?guī)硪黄猨ava,android,MD5加密算法的實現(xiàn)代碼(16位,32位)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-09-09
springboot中非容器類如何獲取配置文件數(shù)據(jù)
這篇文章主要介紹了springboot中非容器類如何獲取配置文件數(shù)據(jù)問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-01-01
java使用Hex編碼解碼實現(xiàn)Aes加密解密功能示例
這篇文章主要介紹了java使用Hex編碼解碼實現(xiàn)Aes加密解密功能,結(jié)合完整實例形式分析了Aes加密解密功能的定義與使用方法,需要的朋友可以參考下2017-01-01
MyBatis模糊查詢報錯:ParserException: not supported.pos&n
本文主要介紹了MyBatis模糊查詢報錯:ParserException: not supported.pos 問題解決,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2025-04-04
Java實現(xiàn)世界上最快的排序算法Timsort的示例代碼
Timsort?是一個混合、穩(wěn)定的排序算法,簡單來說就是歸并排序和二分插入排序算法的混合體,號稱世界上最好的排序算法。本文將詳解Timsort算法是定義與實現(xiàn),需要的可以參考一下2022-07-07

