1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
packagecom.uncle5.pubrub.web.common;
importjava.util.UUID;
importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpSession;
publicfinalclassCsrfTokenManager {
// 隐藏域参数名称
staticfinalString CSRF_PARAM_NAME ="CSRFToken";
// session中csrfToken参数名称
publicstaticfinalString CSRF_TOKEN_FOR_SESSION_ATTR_NAME = CsrfTokenManager.class
.getName() +".tokenval";
privateCsrfTokenManager() {
};
// 在session中创建csrfToken
publicstaticString createTokenForSession(HttpSession session) {
String token =null;
synchronized(session) {
token = (String) session
.getAttribute(CSRF_TOKEN_FOR_SESSION_ATTR_NAME);
if(null== token) {
token = UUID.randomUUID().toString();
session.setAttribute(CSRF_TOKEN_FOR_SESSION_ATTR_NAME, token);
}
}
returntoken;
}
publicstaticString getTokenFromRequest(HttpServletRequest request) {
returnrequest.getParameter(CSRF_PARAM_NAME);
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
packagecom.uncle5.pubrub.web.common;
importjava.util.Map;
importjavax.servlet.http.HttpServletRequest;
importorg.springframework.stereotype.Component;
importorg.springframework.web.servlet.support.RequestDataValueProcessor;
importcom.google.common.collect.Maps;
@Component("requestDataValueProcessor")
publicclassCsrfRequestDataValueProcessorimplementsRequestDataValueProcessor {
@Override
publicString processAction(HttpServletRequest request, String action) {
// TODO 暂时原样返回action
returnaction;
}
@Override
publicString processFormFieldValue(HttpServletRequest request,
String name, String value, String type) {
// TODO 暂时原样返回value
returnvalue;
}
@Override
publicMap<String, String> getExtraHiddenFields(HttpServletRequest request) {
//此处是当使用spring的taglib标签<form:form>创建表单时候,增加的隐藏域参数
Map<String, String> hiddenFields = Maps.newHashMap();
hiddenFields.put(CsrfTokenManager.CSRF_PARAM_NAME,
CsrfTokenManager.createTokenForSession(request.getSession()));
returnhiddenFields;
}
@Override
publicString processUrl(HttpServletRequest request, String url) {
// TODO 暂时原样返回url
returnurl;
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
|
packagecom.uncle5.pubrub.web.security;
importjava.net.URLEncoder;
importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpServletResponse;
importorg.slf4j.Logger;
importorg.slf4j.LoggerFactory;
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.util.StringUtils;
importorg.springframework.web.servlet.handler.HandlerInterceptorAdapter;
importcom.uncle5.pubrub.web.common.CsrfTokenManager;
importcom.uncle5.pubrub.web.common.WebUser;
publicclassCsrfInterceptorextendsHandlerInterceptorAdapter {
privatestaticfinalLogger logger = LoggerFactory
.getLogger(CsrfInterceptor.class);
@Autowired
WebUser webUser;
@Override
publicbooleanpreHandle(HttpServletRequest request,
HttpServletResponse response, Object handler)throwsException {
if("POST".equalsIgnoreCase(request.getMethod())) {
String CsrfToken = CsrfTokenManager.getTokenFromRequest(request);
if(CsrfToken ==null
|| !CsrfToken.equals(request.getSession().getAttribute(
CsrfTokenManager.CSRF_TOKEN_FOR_SESSION_ATTR_NAME))) {
String reLoginUrl ="/login?backurl="
+ URLEncoder.encode(getCurrentUrl(request),"utf-8");
response.sendRedirect(reLoginUrl);
returnfalse;
}
}
returntrue;
}
privateString getCurrentUrl(HttpServletRequest request) {
String currentUrl = request.getRequestURL().toString();
if(!StringUtils.isEmpty(request.getQueryString())) {
currentUrl +="?"+ request.getQueryString();
}
returncurrentUrl;
}
}
|
1
2
3
4
5
6
7
8
|
<!-- 安全拦截用的 -->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mappingpath="/forum/post"/>
<mvc:mappingpath="/forum/reply/*"/>
<beanclass="com.uncle5.pubrub.web.security.CsrfInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
|
1
2
3
4
5
6
7
8
9
|
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<divstyle="margin: 0 auto; border: 1px solid orange; width: 80%; height: 400px;">
<form:formaction="/forum/post"method="post">
<span>标题:</span><inputtype="text"name="title"id="title">
<textareaname="content"id="content"rows="30"cols="40"></textarea>
<inputtype="submit"name="submit"value="submit">
</form:form>
</div>
|
1
2
3
4
5
6
7
8
|
<divstyle="margin: 0 auto; border: 1px solid orange; width: 80%; height: 400px;">
<formid="command"action="/forum/post"method="post">
<span>标题:</span><inputtype="text"name="title"id="title">
<textareaname="content"id="content"rows="30"cols="40"></textarea>
<inputtype="submit"name="submit"value="submit">
<inputtype="hidden"name="CSRFToken"value="35afec82-da7b-449e-9ae9-b38664b5af63"/>
</form>
</div>
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
1.只有当使用spring的form标签时候,才会在渲染jsp页面时候触发
org.springframework.web.servlet.tags.form.FormTag 类中的
@Override
publicintdoEndTag()throwsJspException {
RequestDataValueProcessor processor = getRequestContext().getRequestDataValueProcessor();
ServletRequest request =this.pageContext.getRequest();
if((processor !=null) && (requestinstanceofHttpServletRequest)) {
writeHiddenFields(processor.getExtraHiddenFields((HttpServletRequest) request));
}
this.tagWriter.endTag();
returnEVAL_PAGE;
}
该方法会调用上文中所说的CsrfRequestDataValueProcessor中的创建隐藏域的方法getExtraHiddenFields来创建csrfToken隐藏域。
2.对相关需要进行防范csrf攻击的post请求地址进行拦截,此处是依赖springMVC中提供的拦截器机制来操作的
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/forum/post"/>
<mvc:mapping path="/forum/reply/*"/>
<beanclass="com.uncle5.pubrub.web.security.CsrfInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
当用户访问"/forum/post"这个请求时候,会直接进入CsrfInterceptor的preHandle方法,此处针对post请求
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
packagecom.uncle5.pubrub.web.common;
importjava.util.UUID;
importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpSession;
publicfinalclassCsrfTokenManager {
// 隐藏域参数名称
staticfinalString CSRF_PARAM_NAME ="CSRFToken";
// session中csrfToken参数名称
publicstaticfinalString CSRF_TOKEN_FOR_SESSION_ATTR_NAME = CsrfTokenManager.class
.getName() +".tokenval";
privateCsrfTokenManager() {
};
// 在session中创建csrfToken
publicstaticString createTokenForSession(HttpSession session) {
String token =null;
synchronized(session) {
token = (String) session
.getAttribute(CSRF_TOKEN_FOR_SESSION_ATTR_NAME);
if(null== token) {
token = UUID.randomUUID().toString();
session.setAttribute(CSRF_TOKEN_FOR_SESSION_ATTR_NAME, token);
}
}
returntoken;
}
publicstaticString getTokenFromRequest(HttpServletRequest request) {
returnrequest.getParameter(CSRF_PARAM_NAME);
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
packagecom.uncle5.pubrub.web.common;
importjava.util.Map;
importjavax.servlet.http.HttpServletRequest;
importorg.springframework.stereotype.Component;
importorg.springframework.web.servlet.support.RequestDataValueProcessor;
importcom.google.common.collect.Maps;
@Component("requestDataValueProcessor")
publicclassCsrfRequestDataValueProcessorimplementsRequestDataValueProcessor {
@Override
publicString processAction(HttpServletRequest request, String action) {
// TODO 暂时原样返回action
returnaction;
}
@Override
publicString processFormFieldValue(HttpServletRequest request,
String name, String value, String type) {
// TODO 暂时原样返回value
returnvalue;
}
@Override
publicMap<String, String> getExtraHiddenFields(HttpServletRequest request) {
//此处是当使用spring的taglib标签<form:form>创建表单时候,增加的隐藏域参数
Map<String, String> hiddenFields = Maps.newHashMap();
hiddenFields.put(CsrfTokenManager.CSRF_PARAM_NAME,
CsrfTokenManager.createTokenForSession(request.getSession()));
returnhiddenFields;
}
@Override
publicString processUrl(HttpServletRequest request, String url) {
// TODO 暂时原样返回url
returnurl;
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
|
packagecom.uncle5.pubrub.web.security;
importjava.net.URLEncoder;
importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpServletResponse;
importorg.slf4j.Logger;
importorg.slf4j.LoggerFactory;
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.util.StringUtils;
importorg.springframework.web.servlet.handler.HandlerInterceptorAdapter;
importcom.uncle5.pubrub.web.common.CsrfTokenManager;
importcom.uncle5.pubrub.web.common.WebUser;
publicclassCsrfInterceptorextendsHandlerInterceptorAdapter {
privatestaticfinalLogger logger = LoggerFactory
.getLogger(CsrfInterceptor.class);
@Autowired
WebUser webUser;
@Override
publicbooleanpreHandle(HttpServletRequest request,
HttpServletResponse response, Object handler)throwsException {
if("POST".equalsIgnoreCase(request.getMethod())) {
String CsrfToken = CsrfTokenManager.getTokenFromRequest(request);
if(CsrfToken ==null
|| !CsrfToken.equals(request.getSession().getAttribute(
CsrfTokenManager.CSRF_TOKEN_FOR_SESSION_ATTR_NAME))) {
String reLoginUrl ="/login?backurl="
+ URLEncoder.encode(getCurrentUrl(request),"utf-8");
response.sendRedirect(reLoginUrl);
returnfalse;
}
}
returntrue;
}
privateString getCurrentUrl(HttpServletRequest request) {
String currentUrl = request.getRequestURL().toString();
if(!StringUtils.isEmpty(request.getQueryString())) {
currentUrl +="?"+ request.getQueryString();
}
returncurrentUrl;
}
}
|
1
2
3
4
5
6
7
8
|
<!-- 安全拦截用的 -->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mappingpath="/forum/post"/>
<mvc:mappingpath="/forum/reply/*"/>
<beanclass="com.uncle5.pubrub.web.security.CsrfInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
|
1
2
3
4
5
6
7
8
9
|
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<divstyle="margin: 0 auto; border: 1px solid orange; width: 80%; height: 400px;">
<form:formaction="/forum/post"method="post">
<span>标题:</span><inputtype="text"name="title"id="title">
<textareaname="content"id="content"rows="30"cols="40"></textarea>
<inputtype="submit"name="submit"value="submit">
</form:form>
</div>
|
1
2
3
4
5
6
7
8
|
<divstyle="margin: 0 auto; border: 1px solid orange; width: 80%; height: 400px;">
<formid="command"action="/forum/post"method="post">
<span>标题:</span><inputtype="text"name="title"id="title">
<textareaname="content"id="content"rows="30"cols="40"></textarea>
<inputtype="submit"name="submit"value="submit">
<inputtype="hidden"name="CSRFToken"value="35afec82-da7b-449e-9ae9-b38664b5af63"/>
</form>
</div>
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
1.只有当使用spring的form标签时候,才会在渲染jsp页面时候触发
org.springframework.web.servlet.tags.form.FormTag 类中的
@Override
publicintdoEndTag()throwsJspException {
RequestDataValueProcessor processor = getRequestContext().getRequestDataValueProcessor();
ServletRequest request =this.pageContext.getRequest();
if((processor !=null) && (requestinstanceofHttpServletRequest)) {
writeHiddenFields(processor.getExtraHiddenFields((HttpServletRequest) request));
}
this.tagWriter.endTag();
returnEVAL_PAGE;
}
该方法会调用上文中所说的CsrfRequestDataValueProcessor中的创建隐藏域的方法getExtraHiddenFields来创建csrfToken隐藏域。
2.对相关需要进行防范csrf攻击的post请求地址进行拦截,此处是依赖springMVC中提供的拦截器机制来操作的
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/forum/post"/>
<mvc:mapping path="/forum/reply/*"/>
<beanclass="com.uncle5.pubrub.web.security.CsrfInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
当用户访问"/forum/post"这个请求时候,会直接进入CsrfInterceptor的preHandle方法,此处针对post请求
|