防止跨站请求伪造(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注解*/@Overridepublic 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) {//需要验证tokenboolean 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;}@Overridepublic 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)
转载于:https://www.cnblogs.com/yangzhilong/p/4917840.html
防止跨站请求伪造(CSRF)攻击 和 防重复提交 的方法的实现相关推荐
- MVC安全:ajax表单提交切记加上AntiForgeryToken防止跨站请求伪造 (CSRF)攻击
因为项目使用的是mvc的框架,前端使用的是metronic bootstrap框架,所以在处理表单的提交时就用了ajax的方式进行提交,这样前端js也方便封装,实现代码复用. 先看看js端的相关代码 ...
- CSRF(跨站请求伪造)攻击 --
CSRF(跨站请求伪造)攻击 CSRF(Cross Site Request Forgery,跨站请求伪造)是一种近年来才逐渐被大众了解的网络攻击方式,又被称为One-Click Attack或Ses ...
- 跨站请求伪造(CSRF)-简述
跨站请求伪造(CSRF)-简述 跨站请求伪造(英语:Cross-site request forgery),也被称为 one-click attack 或者 session riding,通常缩写为 ...
- 带你刷burpsuite官方网络安全学院靶场(练兵场)之客户端漏洞——跨站请求伪造(CSRF)专题
介绍 PortSwigger是信息安全从业者必备工具burpsuite的发行商,作为网络空间安全的领导者,他们为信息安全初学者提供了一个在线的网络安全学院(也称练兵场),在讲解相关漏洞的同时还配套了相 ...
- django16: csrf跨站请求伪造/CSRF相关装饰器
CSRF 即跨站请求攻击 跨站请求伪造csrf钓鱼网站本质搭建一个跟正常网站一模一样的页面用户在该页面上完成转账功能转账的请求确实是朝着正常网站的服务端提交唯一不同的在于收款账户人不同给用户书写for ...
- 我要学ASP.NET MVC 3.0(十三): MVC 3.0 防止跨站点请求伪造 (CSRF) 攻击
我要学ASP.NET MVC 3.0(十三): MVC 3.0 防止跨站点请求伪造 (CSRF) 攻击 概述 众所周知,ASP.Net MVC程序在浏览器运行时产生了标准的Html标签,包括 ...
- 浅谈跨站请求伪造(CSRF)
浅谈跨站请求伪造(CSRF) 这里简单的记录一下CSRF漏洞~~ 什么是CSRF? CSRF(Cross-Site Request Forgery,跨站点伪造请求)是一种网络攻击方式,该攻击可 ...
- Nginx配置valid_referer解决跨站请求伪造(CSRF)
Nginx配置valid_referer解决跨站请求伪造(CSRF) 文章目录 Nginx配置valid_referer解决跨站请求伪造(CSRF) 漏洞说明 漏洞描述 危害等级 修复建议 漏洞复现 ...
- csrf防御 php,跨站请求伪造CSRF的防御实例(PHP版本)
跨站请求伪造CSRF的防御:One-Time Tokens(不同的表单包含一个不同的伪随机值) 在实现One-Time Tokens时,需要注意一点:就是"并行会话的兼容".如果用 ...
最新文章
- arcgis server 开发
- C language day1
- 深入线程池的问题连环炮
- 云图说丨初识数据工坊DWR
- 几大 Git 平台仓库被劫,黑客欲勒索比特币
- 认知无线电----能量检测法原理介绍及MATLAB实现
- [原创]FlashFXP打造自动镜像更新
- TFTPD32不能传输数据的解决与尝试
- 谈MDM主数据管理系统、BI、大数据、SOA之间的关系
- win10如何删除微软拼音输入法
- linux nfs不在同一个网络,NFS共享机制
- NVIDIA Jetson TK1学习与开发(三):图文详解Jetson TK1平台搭建
- 【学透二叉树-二叉搜索树(二叉树)的最近公共祖先】
- QtCreator生成标准多行函数或者类详细注释
- vscode 中python不能跳转问题
- mysql 灾备方案_mysql数据库灾备方案
- C、C++、java的区别
- Revit复制轴网和标高
- 阿里企业邮箱-PC加载outlook插件
- ios开发之app内启动用户评价