防止跨站请求伪造(CSRF)攻击 和 防重复提交 的方法的实现
CSRF的概念可以参考:http://netsecurity.51cto.com/art/200812/102951.htm
本文介绍的是基于spring拦截器的Spring MVC实现
首先配置拦截器:
<mvc:interceptors>
<mvc:interceptor>
<!-- 匹配的是url路径, 如果不配置或/**,将拦截所有的Controller -->
<mvc:mapping path="/xxx/**" />
<bean class="com.xxx.SecurityTokenInterceptor"></bean>
</mvc:interceptor>
<!-- 当设置多个拦截器时,先按顺序调用preHandle方法,然后逆序调用每个拦截器的postHandle和afterCompletion方法 -->
</mvc:interceptors>
SecurityTokenInterceptor代码如下
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; /**
* 防止重复提交过滤器
*
*/ public class SecurityTokenInterceptor extends HandlerInterceptorAdapter {
private static final Logger duplicateAvoidLOG = LoggerFactory
.getLogger(SecurityTokenInterceptor.class); public static final String DUPLICATEAVOID_TOKEN = "duplicateAvoid_token";
public static final String PAGE_DUPLICATEAVOID_TOKEN = "sumbit_token";
// private static final ConcurrentMap<String, String> tokenMap = new ConcurrentHashMap<String, String>();
public static final int expressTime = 60 * 60 * 24 * 5;
public static final String TOKEN_ERROR_CODE ="duplicate_Error";
public static final ThreadLocal<String> threadtoken = new ThreadLocal<String>(); /**
* 前置处理器中 检查 DuplicateAvoid注解
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler){
duplicateAvoidLOG.info("DuplicateAvoidSubmitInterceptor start,[client:" + getRemoteHost(request) + ",url:"
+ request.getServletPath() + "]");
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod .getMethod();
SecurityToken annotation = method.getAnnotation(SecurityToken.class);
duplicateAvoidLOG.info("method infos,[method:" + method.getName() + "]");
if (annotation != null) {
//需要验证token
boolean needValidateToken = annotation.validateToken();
if(needValidateToken){
if(isRepeatSubmit(request)){
duplicateAvoidLOG.error("please don't repeat submit,[client:" + getRemoteHost(request) + ",url:"
+ request.getServletPath() + "]");
request.getSession().setAttribute(TOKEN_ERROR_CODE, "please don't repeat submit");
String fuction = annotation.ajaxFailCallBack();
String responStr = "{\"duplicate\":\"true\",\"callback\":\""+fuction+"\"}";
try {
response.getOutputStream().write(responStr.getBytes()); response.getOutputStream().flush();
response.getOutputStream().close();
} catch (IOException e) {
duplicateAvoidLOG.error("repeat submit ,[client:" + getRemoteHost(request) + ",url:"
+ request.getServletPath() + "]");
e.printStackTrace();
}
return false;
}
request.getSession(false).removeAttribute(DUPLICATEAVOID_TOKEN);
}
//需要保存token,先产生token 保存到response的cookie中,服务器端保存在业务方法调用后保存
boolean needSaveToken = annotation.generateToken();
if (needSaveToken) {
String token = UUID.randomUUID().toString();
Cookie tokenCookie = new Cookie(PAGE_DUPLICATEAVOID_TOKEN, token);
tokenCookie.setMaxAge(expressTime);
tokenCookie.setPath("/");
response.addCookie(tokenCookie);
// response.addHeader(PAGE_DUPLICATEAVOID_TOKEN, token);
threadtoken.set(token);
}
}
}
return true;
} @Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
duplicateAvoidLOG.info("DuplicateAvoidSubmitInterceptor postHandle,[client:" + getRemoteHost(request) + ",url:"
+ request.getServletPath() + "]"+",token ="+ threadtoken.get());
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod .getMethod();
SecurityToken annotation = method.getAnnotation(SecurityToken.class);
duplicateAvoidLOG.info("method infos,[method:" + method.getName() + "]");
if (annotation != null) {
//保存token,到服务器的session中
boolean needSaveToken = annotation.generateToken();
if (needSaveToken) {
String token = threadtoken.get();
threadtoken.set("");
if (!StringUtils.isEmpty(token)) {
request.getSession().setAttribute(DUPLICATEAVOID_TOKEN, token);
}
}
}
}
} private boolean isRepeatSubmit(HttpServletRequest request){ String serverToken = (String) request.getSession(false).getAttribute(DUPLICATEAVOID_TOKEN);
Cookie cookies[]=request.getCookies();
Cookie tokenCookie=null;
String clinetToken = null;
for (int i = 0; i < cookies.length; i++) {
tokenCookie = cookies[i];
if(PAGE_DUPLICATEAVOID_TOKEN.equals(tokenCookie.getName())){
clinetToken = tokenCookie.getValue();
}
}
if (StringUtils.isEmpty(clinetToken)) {
clinetToken = request.getParameter(PAGE_DUPLICATEAVOID_TOKEN);
}
duplicateAvoidLOG.info("isRepeatSubmit ,[serverToken:" + serverToken + ",clinetToken:"
+ clinetToken + "]");
if (StringUtils.isEmpty(serverToken) || StringUtils.isEmpty(clinetToken)) {
return true;
}
if (!serverToken.equals(clinetToken)) {
return true;
} return false;
} private String getRemoteHost(HttpServletRequest request){
String ip = request.getHeader("x-forwarded-for");
if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){
ip = request.getHeader("Proxy-Client-IP");
}
if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){
ip = request.getHeader("WL-Proxy-Client-IP");
}
if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){
ip = request.getRemoteAddr();
}
return ip.equals("0:0:0:0:0:0:0:1")?"127.0.0.1":ip;
} }
SecurityToken.java
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import org.springframework.beans.factory.annotation.Required; /**
* <p>
* 防止重复提交注解,用于方法上<br/>
* 在control方法上,设置needSaveToken()为true,此时拦截器会在Session中保存一个token<br/>
* 在control方法上,设置needValidateToken()为true,此时拦截器会在Session中验证token,并且删除token<br/>
* 需要防止重复提交的页面中需要添加<br/>
* </p>
*
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SecurityToken {
boolean generateToken() default false;
boolean validateToken() default false;
//ajax请求如果重复提交后默认的JS回调方法
String ajaxFailCallBack() default "HandleTokenFail";
}
使用方法:
在跳转到需要防止重复提交的页面的controller上加注解来generateToken, 然后在业务方法的controller上加入注解validateToken(如果此方法跳转(或者ajax)执行完后下面的页面还要继续防重复可以在此方法上再加generateToken)
防止跨站请求伪造(CSRF)攻击 和 防重复提交 的方法的实现的更多相关文章
- 跨站请求伪造(CSRF)攻击原理解析:比你所想的更危险
跨站请求伪造(CSRF)攻击原理解析:比你所想的更危险 跨站请求伪造(Cross-Site Request Forgery)或许是最令人难以理解的一种攻击方式了,但也正因如此,它的危险性也被人们所低估 ...
- 跨站请求伪造(CSRF攻击)理解
一 概念 你这可以这么理解CSRF攻击:攻击者盗用了你的身份,以你的名义发送恶意请求.CSRF能够做的事情包括:以你名义发送邮件,发消息,盗取你的账号,甚至于购买商品,虚拟货币转账......造成的 ...
- CSRF(跨站请求伪造)攻击
CSRF(跨站请求伪造)攻击 CSRF(Cross Site Request Forgery,跨站请求伪造)是一种近年来才逐渐被大众了解的网络攻击方式,又被称为One-Click Attack或Ses ...
- PHP安全编程:跨站请求伪造CSRF的防御(转)
跨站请求伪造(CSRF)是一种允许攻击者通过受害者发送任意HTTP请求的一类攻击方法.此处所指的受害者是一个不知情的同谋,所有的伪造请求都由他发起,而不是攻击者.这样,很你就很难确定哪些请求是属于跨站 ...
- 跨站请求伪造(CSRF)-简述
跨站请求伪造(CSRF)-简述 跨站请求伪造(英语:Cross-site request forgery),也被称为 one-click attack 或者 session riding,通常缩写为 ...
- django之跨站请求伪造csrf
目录 跨站请求伪造 csrf 钓鱼网站 模拟实现 针对form表单 ajax请求 csrf相关的两个装饰器 跨站请求伪造 csrf 钓鱼网站 就类似于你搭建了一个跟银行一模一样的web页面 , 用户在 ...
- 跨站请求伪造 CSRF / XSRF<一:介绍>
跨站请求伪造(英语:Cross-site request forgery),也被称为 one-click attack 或者 session riding,通常缩写为 CSRF 或者 XSRF, 是一 ...
- mvc3.0防止跨站点请求伪造(CSRF)攻击
众所周知,asp.net mvc程序在浏览器运行是产生标准的Html标签,包括浏览器要发送的关键数据等内容都在html内容里面.听起来不错,但是假如我们伪造类似的html内容,更改里面的关键数据,在浏 ...
- 跨站请求伪造CSRF(Cross-site request forgery)
CSRF(Cross-site request forgery)跨站请求伪造,也被称为“One Click Attack”或者Session Riding,通常缩写为CSRF或者XSRF,是一种对网站 ...
随机推荐
- Could not install packages due to an Environment Error: [Errno 13] Permission denied 解决方案
执行pip install 报错如下: Could not install packages due to an Environment Error: [Errno 13] Permission de ...
- 基于图的图像分割(Graph-Based Image Segmentation)
一.介绍 基于图的图像分割(Graph-Based Image Segmentation),论文<Efficient Graph-Based Image Segmentation>,P. ...
- HDOJ 4869 Turn the pokers
最后的结果中正面向上的奇偶性是一定的,计算出正面向上的范围low,up 结果即为 C(m.low)+ C(m.low+2) +.... + C(m,up) ,用逆元取模 Turn the pokers ...
- 第四章 logstash - type与tags属性
参考:http://kibana.logstash.es/content/logstash/plugins/input/stdin.html 最常用法: input { stdin { tags =& ...
- how to fix bug in daily work
0 QE will begin test the product when system is stable. so they may log a lot of issues, and our dai ...
- iOS开发-UICollectionView实现瀑布流
关于瀑布流的实现网上有很多种解法,自定义控件,TableView+ScrollView,UICollectionView是iOS6发布之后用于展示集合视图,算起来已经发布三年左右了,不过知识点是不变的 ...
- 创建SQL作业错误的解决方法(不能将值 NULL 插入列 'owner_sid',表 'msdb.dbo.sysjobs';列不允许有空值。)
在用SQL语句创建SQL Server作业时有时出现如下错误: 消息 515,级别 16,状态 2,过程 sp_add_job,第 137 行 不能将值 NULL 插入列 'owner_sid',表 ...
- javascript——select 标签的使用
<% String state = (String) request.getAttribute("state"); String day = (String) request ...
- vue版本冲突解决办法
ERROR in Vue packages version mismatch: vue@2.1.4 vue-template-compiler@2.1.5 This may cause things ...
- Wifidog的协议梳理
上篇文章结合wifidog的协议,讲解了如何实现wifi认证.这篇文章会详细讲解一下wifidog的协议. wifidog的认证流程图 用户连接WIFI会跳转到以下地址: 1 2 3 4 5 6 7 ...