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)攻击 和 防重复提交 的方法的实现的更多相关文章

  1. 跨站请求伪造(CSRF)攻击原理解析:比你所想的更危险

    跨站请求伪造(CSRF)攻击原理解析:比你所想的更危险 跨站请求伪造(Cross-Site Request Forgery)或许是最令人难以理解的一种攻击方式了,但也正因如此,它的危险性也被人们所低估 ...

  2. 跨站请求伪造(CSRF攻击)理解

    一  概念 你这可以这么理解CSRF攻击:攻击者盗用了你的身份,以你的名义发送恶意请求.CSRF能够做的事情包括:以你名义发送邮件,发消息,盗取你的账号,甚至于购买商品,虚拟货币转账......造成的 ...

  3. CSRF(跨站请求伪造)攻击

    CSRF(跨站请求伪造)攻击 CSRF(Cross Site Request Forgery,跨站请求伪造)是一种近年来才逐渐被大众了解的网络攻击方式,又被称为One-Click Attack或Ses ...

  4. PHP安全编程:跨站请求伪造CSRF的防御(转)

    跨站请求伪造(CSRF)是一种允许攻击者通过受害者发送任意HTTP请求的一类攻击方法.此处所指的受害者是一个不知情的同谋,所有的伪造请求都由他发起,而不是攻击者.这样,很你就很难确定哪些请求是属于跨站 ...

  5. 跨站请求伪造(CSRF)-简述

    跨站请求伪造(CSRF)-简述 跨站请求伪造(英语:Cross-site request forgery),也被称为 one-click attack 或者 session riding,通常缩写为 ...

  6. django之跨站请求伪造csrf

    目录 跨站请求伪造 csrf 钓鱼网站 模拟实现 针对form表单 ajax请求 csrf相关的两个装饰器 跨站请求伪造 csrf 钓鱼网站 就类似于你搭建了一个跟银行一模一样的web页面 , 用户在 ...

  7. 跨站请求伪造 CSRF / XSRF<一:介绍>

    跨站请求伪造(英语:Cross-site request forgery),也被称为 one-click attack 或者 session riding,通常缩写为 CSRF 或者 XSRF, 是一 ...

  8. mvc3.0防止跨站点请求伪造(CSRF)攻击

    众所周知,asp.net mvc程序在浏览器运行是产生标准的Html标签,包括浏览器要发送的关键数据等内容都在html内容里面.听起来不错,但是假如我们伪造类似的html内容,更改里面的关键数据,在浏 ...

  9. 跨站请求伪造CSRF(Cross-site request forgery)

    CSRF(Cross-site request forgery)跨站请求伪造,也被称为“One Click Attack”或者Session Riding,通常缩写为CSRF或者XSRF,是一种对网站 ...

随机推荐

  1. [Link]Gearman分布式任务处理系统

    http://blog.csdn.net/jiao_fuyou/article/category/1745977 http://www.cnblogs.com/cocowool/archive/201 ...

  2. ArcGIS Server 10 for java 注册SOE出现的问题

    一个SOE 需要register,但是报错 Manage Extensions Refresh Unable to register extension. com.esri.arcgis.intero ...

  3. 奇怪吸引子---Coullet

    奇怪吸引子是混沌学的重要组成理论,用于演化过程的终极状态,具有如下特征:终极性.稳定性.吸引性.吸引子是一个数学概念,描写运动的收敛类型.它是指这样的一个集合,当时间趋于无穷大时,在任何一个有界集上出 ...

  4. scp命令:服务器间远程复制代码

    scp是secure copy的简写,用于在Linux下进行远程拷贝文件的命令,和它类似的命令有cp,不过cp只是在本机进行拷贝不能跨服务器,而且scp传输是加密的.可能会稍微影响一下速度.当你服务器 ...

  5. 东芝发布运行Win 10的AR眼镜,它和Google Glass企业版有哪些异同?

    https://www.leiphone.com/news/201803/Tw0nrq6vGDIvbmXr.html 雷锋网(公众号:雷锋网)获悉,3月13日,东芝发布新AR眼镜dynaEdge AR ...

  6. RxJava RxPermissions 动态权限 简介 原理 案例 MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  7. ORM数据库框架 LitePal SQLite MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  8. 初学 Delphi 嵌入汇编[1] - 汇编语言与机器语言

    非科班出身, 现在才接触汇编, 惭愧呀, 好好学! 主选课本是清华大学王爽老师的<汇编语言>. 推荐 王爽老师的汇编网 汇编语言之前是机器语言. 机器语言是机器指令的集合, 机器指令是一系 ...

  9. 【Spark】Spark-Redis连接池

    Spark-Redis连接池 jedispool returnresource 遭废弃 用 什么替代_百度知道 spark-stream 访问 Redis数据库示例 - 阿里云 [Redis]Java ...

  10. jdbc操作数据库并自动获取字段类型

    //获取改功能编码的关联功能 public void getLinkdb(String gnbianma){ PreparedStatement pstmt = null; ResultSet rs ...