詳解利用spring-security解決CSRF問(wèn)題
CSRF介紹
CSRF(Cross-site request forgery),中文名稱:跨站請(qǐng)求偽造,也被稱為:one click attack/session riding,縮寫(xiě)為:CSRF/XSRF。
具體SCRF的介紹和攻擊方式請(qǐng)參看百度百科的介紹和一位大牛的分析:
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的實(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)題可以留言交流。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Java實(shí)現(xiàn)合并兩個(gè)有序序列算法示例
這篇文章主要介紹了Java實(shí)現(xiàn)合并兩個(gè)有序序列算法,簡(jiǎn)單描述了序列合并算法的原理與java合并有序序列的具體操作步驟及相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2017-09-09java,android,MD5加密算法的實(shí)現(xiàn)代碼(16位,32位)
下面小編就為大家?guī)?lái)一篇java,android,MD5加密算法的實(shí)現(xiàn)代碼(16位,32位)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-09-09Java中JSON字符串反序列化(動(dòng)態(tài)泛型)
文章討論了在定時(shí)任務(wù)中使用反射調(diào)用目標(biāo)對(duì)象時(shí)處理動(dòng)態(tài)參數(shù)的問(wèn)題,通過(guò)將方法參數(shù)存儲(chǔ)為JSON字符串并進(jìn)行反序列化,可以實(shí)現(xiàn)動(dòng)態(tài)調(diào)用,然而,這種方式容易導(dǎo)致內(nèi)存溢出(OOM),這篇文章主要介紹了JSON字符串反序列化?動(dòng)態(tài)泛型,需要的朋友可以參考下2024-12-12springboot中非容器類如何獲取配置文件數(shù)據(jù)
這篇文章主要介紹了springboot中非容器類如何獲取配置文件數(shù)據(jù)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01java使用Hex編碼解碼實(shí)現(xiàn)Aes加密解密功能示例
這篇文章主要介紹了java使用Hex編碼解碼實(shí)現(xiàn)Aes加密解密功能,結(jié)合完整實(shí)例形式分析了Aes加密解密功能的定義與使用方法,需要的朋友可以參考下2017-01-01SpringBoot java-jar命令行啟動(dòng)原理解析
這篇文章主要介紹了SpringBoot java-jar命令行啟動(dòng)原理解析,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-07-07MyBatis模糊查詢報(bào)錯(cuò):ParserException: not supported.pos&n
本文主要介紹了MyBatis模糊查詢報(bào)錯(cuò):ParserException: not supported.pos 問(wèn)題解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2025-04-04Java實(shí)現(xiàn)世界上最快的排序算法Timsort的示例代碼
Timsort?是一個(gè)混合、穩(wěn)定的排序算法,簡(jiǎn)單來(lái)說(shuō)就是歸并排序和二分插入排序算法的混合體,號(hào)稱世界上最好的排序算法。本文將詳解Timsort算法是定義與實(shí)現(xiàn),需要的可以參考一下2022-07-07