防止跨站请求伪造(CSRF)攻击 和 防重复提交 的方法的实现

时间:2022-08-30 23:32:16

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. 跨站请求伪造&lpar;CSRF&rpar;攻击原理解析:比你所想的更危险

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

  2. 跨站请求伪造&lpar;CSRF攻击&rpar;理解

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

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

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

  4. PHP安全编程:跨站请求伪造CSRF的防御&lpar;转&rpar;

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

  5. 跨站请求伪造&lpar;CSRF&rpar;-简述

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

  6. django之跨站请求伪造csrf

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

  7. 跨站请求伪造 CSRF &sol; XSRF&lt&semi;一:介绍&gt&semi;

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

  8. mvc3&period;0防止跨站点请求伪造&lpar;CSRF&rpar;攻击

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

  9. 跨站请求伪造CSRF&lpar;Cross-site request forgery&rpar;

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

随机推荐

  1. 《第一本docker书》- 第一章笔记

    环境: Ubuntu 14.04.2 LTS (GNU/Linux 3.16.0-30-generic i686) 第一章: 1 Docker客户端和服务器 2 Docker镜像 添加一个文件,执行一 ...

  2. input 获取当前id,name

    <input name=" src="toright.png" value="mp3"> <script language=&quo ...

  3. 【leetcode】Single Number II (medium) &starf; 自己没做出来&period;&period;&period;&period;

    Given an array of integers, every element appears three times except for one. Find that single one. ...

  4. C&num; Dictionary用法总结

    转自:http://www.cnblogs.com/linlf03/archive/2011/12/09/2282574.html http://www.cnblogs.com/linzheng/ar ...

  5. 基于visual Studio2013解决C语言竞赛题之1080填运算符

        题目 解决代码及点评 /************************************************************************/ /* ...

  6. openSUSE 国内镜像摘要

    1. 北交(BJTU): http://mirror.bjtu.edu.cn/opensuse http://mirror.bjtu.edu.cn/packman/suse 2. 华中科大(HUST) ...

  7. 使用 vue-i18n 切换中英文

    兼容性: 支持 Vue.js 2.x 以上版本 安装方法:(此处只演示 npm) npm install vue-i18n 使用方法: 1.在 main.js 中引入 vue-i18n (前提是要先引 ...

  8. App上线Check List

    1.umeng统计.安全加固等第三方功能到位,本次上线市场列表选择好.2.后台数据库模式准备3.后台数据库数据准备4.后台应用服务器运行检查5.前端界面信息要正确6.前端交互不能出现过度卡顿和经常闪退 ...

  9. python3 特性

    切片: 就是可以取到对象中任意位置的元素,[start:end:interval]:字符串.列表.元组可切片,字典.set()不可: L[:] #复制原L L[:10] #前十个 L[-10:] #后 ...

  10. &lbrack;转载&rsqb;localStorage使用总结

    一.什么是localStorage.sessionStorage 在HTML5中,新加入了一个localStorage特性,这个特性主要是用来作为本地存储来使用的,解决了cookie存储空间不足的问题 ...