防止跨站请求伪造(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,是一种对网站 ...
随机推荐
- golang的日志系统log和glog
go语言有一个标准库,log,提供了最基本的日志功能,但是没有什么高级的功能,如果需要高级的特性,可以选择glog或log4go. 参考:https://cloud.tencent.com/devel ...
- 28个HTML5特征、窍门和技术
原文地址: http://www.zhangxinxu.com/wordpress/2010/08/%E7%BF%BB%E8%AF%91-%E4%BD%A0%E5%BF%85%E9%A1%BB%E7 ...
- const 变量修饰 研究
#include<stdio.h> #include<iostream> using namespace std; struct A { ;} ;} }; int main() ...
- 下载Sourceforge等国内无法下载站点文件的另一种方法
Sourceforge是一些开源软件经常用到的网站,然而国内的网站一直不稳定,如今是可以访问,但是一直无法下载,今天发现了一种新方法那就是http://www.mirrorservice.org 这个 ...
- CubieBoard 简单入门
大约一个月之前折腾的部分记录,当时没有完全完成,就着手其他事情了,这是存在Live Writer中的草稿,先发出来吧,后来花了一段时间移植Qt,一直遇到了点问题,并没有完全跑通,后续估计也没有时间再继 ...
- 网络编程之webclient和httpwebrequest的使用
(1)Lambda表达式 “Lambda表达式”是一个匿名函数,它可以包含表达式和语句,并且可用于创建委托或表达式树类型. 所有 Lambda 表达式都使用 Lambda 运算符 =>,该运算符 ...
- 第三十一章 elk(2)- 第二种架构(最常用架构)
参考:http://linuxg.blog.51cto.com/4410110/1761757 最常用架构: 一.安装redis 1.下载:http://redis.io/download 2.解压后 ...
- webGl中实现clipplane
webGl中实现clipplane 参考:调用glClipPlane()函数所执行的裁剪是在视觉坐标中完成的,而不是在裁剪坐标中进行的https://blog.csdn.net/shengwenj/a ...
- solr建立pdf/word/excel索引的方法
PS: 本文假设你已经成功的搭建了一个Solr服务器步骤如下:(1)准备好一份Solr的源码,假设现在保存在c:\apache-solr-1.4.1\目录下(2)从https://issues.apa ...
- 《Hadoop大数据架构与实践》学习笔记
学习慕课网的视频:Hadoop大数据平台架构与实践--基础篇http://www.imooc.com/learn/391 一.第一章 #,Hadoop的两大核心: #,HDFS,分布式文件系统 ...