zoukankan      html  css  js  c++  java
  • Spring mvc拦截器防御CSRF攻击

    CSRF(具体参考百度百科)

          CSRF(Cross-site request forgery跨站请求伪造,也被称为“One Click Attack”或者Session Riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。尽管听起来像跨站脚本(XSS),但它与XSS非常不同,并且攻击方式几乎相左。XSS利用站点内的信任用户,而CSRF则通过伪装来自受信任用户的请求来利用受信任的网站。与XSS攻击相比,CSRF攻击往往不大流行(因此对其进行防范的资源也相当稀少)和难以防范,所以被认为比XSS更具危险性。

    具体思路:

    1、跳转页面前生成随机token,并存放在session中

    2、form中将token放在隐藏域中,保存时将token放头部一起提交

    3、获取头部token,与session中的token比较,一致则通过,否则不予提交

    4、生成新的token,并传给前端

    1、拦截器配置

    	<mvc:interceptors>
    	    <!-- csrf攻击防御 -->
    		<mvc:interceptor>
    			<!-- 需拦截的地址 -->
    			<mvc:mapping path="/**" />
    			<!-- 需排除拦截的地址 -->
    			<mvc:exclude-mapping path="/resources/**" />
    			<bean class="com.cnpc.framework.interceptor.CSRFInterceptor" />
    		</mvc:interceptor>
    	</mvc:interceptors>
    

    2拦截器实现 CSRFInterceptor.java

    package com.cnpc.framework.interceptor;
    
    import java.io.OutputStream;
    import java.io.PrintWriter;
    import java.lang.reflect.Method;
    import java.util.HashMap;
    import java.util.Map;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.springframework.web.method.HandlerMethod;
    import org.springframework.web.servlet.ModelAndView;
    import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
    
    import com.alibaba.fastjson.JSONObject;
    import com.cnpc.framework.annotation.RefreshCSRFToken;
    import com.cnpc.framework.annotation.VerifyCSRFToken;
    import com.cnpc.framework.base.pojo.ResultCode;
    import com.cnpc.framework.constant.CodeConstant;
    import com.cnpc.framework.utils.CSRFTokenUtil;
    import com.cnpc.framework.utils.StrUtil;
    
    /**
     * CSRFInterceptor 防止跨站请求伪造拦截器
     *
     * @author billJiang 2016年10月6日 下午8:14:40
     */
    public class CSRFInterceptor extends HandlerInterceptorAdapter {
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    
            System.out.println("---------->" + request.getRequestURI());
            System.out.println(request.getHeader("X-Requested-With"));
            // 提交表单token 校验
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Method method = handlerMethod.getMethod();
            VerifyCSRFToken verifyCSRFToken = method.getAnnotation(VerifyCSRFToken.class);
            // 如果配置了校验csrf token校验,则校验
            if (verifyCSRFToken != null) {
                // 是否为Ajax标志
                String xrq = request.getHeader("X-Requested-With");
                // 非法的跨站请求校验
                if (verifyCSRFToken.verify() && !verifyCSRFToken(request)) {
                    if (StrUtil.isEmpty(xrq)) {
                        // form表单提交,url get方式,刷新csrftoken并跳转提示页面
                        String csrftoken = CSRFTokenUtil.generate(request);
                        request.getSession().setAttribute("CSRFToken", csrftoken);
                        response.setContentType("application/json;charset=UTF-8");
                        PrintWriter out = response.getWriter();
                        out.print("非法请求");
                        response.flushBuffer();
                        return false;
                    } else {
                        // 刷新CSRFToken,返回错误码,用于ajax处理,可自定义
                        String csrftoken = CSRFTokenUtil.generate(request);
                        request.getSession().setAttribute("CSRFToken", csrftoken);
                        ResultCode rc = CodeConstant.CSRF_ERROR;
                        response.setContentType("application/json;charset=UTF-8");
                        PrintWriter out = response.getWriter();
                        out.print(JSONObject.toJSONString(rc));
                        response.flushBuffer();
                        return false;
                    }
                }
    
            }
            return true;
        }
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
                throws Exception {
    
            // 第一次生成token
            if (modelAndView != null) {
                if (request.getSession(false) == null || StrUtil.isEmpty((String) request.getSession(false).getAttribute("CSRFToken"))) {
                    request.getSession().setAttribute("CSRFToken", CSRFTokenUtil.generate(request));
                    return;
                }
            }
            // 刷新token
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Method method = handlerMethod.getMethod();
            RefreshCSRFToken refreshAnnotation = method.getAnnotation(RefreshCSRFToken.class);
    
            // 跳转到一个新页面 刷新token
            String xrq = request.getHeader("X-Requested-With");
            if (refreshAnnotation != null && refreshAnnotation.refresh() && StrUtil.isEmpty(xrq)) {
                request.getSession().setAttribute("CSRFToken", CSRFTokenUtil.generate(request));
                return;
            }
    
            // 校验成功 刷新token 可以防止重复提交
            VerifyCSRFToken verifyAnnotation = method.getAnnotation(VerifyCSRFToken.class);
            if (verifyAnnotation != null) {
                if (verifyAnnotation.verify()) {
                    if (StrUtil.isEmpty(xrq)) {
                        request.getSession().setAttribute("CSRFToken", CSRFTokenUtil.generate(request));
                    } else {
                        Map<String, String> map = new HashMap<String, String>();
                        map.put("CSRFToken", CSRFTokenUtil.generate(request));
                        response.setContentType("application/json;charset=UTF-8");
                        OutputStream out = response.getOutputStream();
                        out.write((",'csrf':" + JSONObject.toJSONString(map) + "}").getBytes("UTF-8"));
                    }
                }
            }
        }
    
        /**
         * 处理跨站请求伪造 针对需要登录后才能处理的请求,验证CSRFToken校验
         * 
         * @param request
         */
        protected boolean verifyCSRFToken(HttpServletRequest request) {
    
            // 请求中的CSRFToken
            String requstCSRFToken = request.getHeader("CSRFToken");
            if (StrUtil.isEmpty(requstCSRFToken)) {
                return false;
            }
            String sessionCSRFToken = (String) request.getSession().getAttribute("CSRFToken");
            if (StrUtil.isEmpty(sessionCSRFToken)) {
                return false;
            }
            return requstCSRFToken.equals(sessionCSRFToken);
        }
    }
    

     3.CSRFTokenUtil.java 生成随机系列号

    package com.cnpc.framework.utils;
    
    import java.util.UUID;
    
    import javax.servlet.http.HttpServletRequest;
    
    public class CSRFTokenUtil {
    
        public static String generate(HttpServletRequest request) {
    
            return UUID.randomUUID().toString();
        }
    
    }
    

      4、ResultCode

    package com.cnpc.framework.base.pojo;
    
    public class ResultCode {
    
        private String code;
    
        private String message;
    
        public ResultCode(String code, String message) {
    
            this.code = code;
            this.message = message;
        }
    
        public String getCode() {
    
            return code;
        }
    
        public void setCode(String code) {
    
            this.code = code;
        }
    
        public String getMessage() {
    
            return message;
        }
    
        public void setMessage(String message) {
    
            this.message = message;
        }
    
    }
    

      5、CodeConstant.java

    package com.cnpc.framework.constant;
    
    import com.cnpc.framework.base.pojo.ResultCode;
    
    public class CodeConstant {
    
        public final static ResultCode CSRF_ERROR = new ResultCode("101", "CSRF ERROR:无效的token,或者token过期");
    }
    

      6、ajax提交方法

    function ajaxPost(url, params, callback) {
    	var result = null;
        var headers={};
        headers['CSRFToken']=$("#csrftoken").val();
        
    	$.ajax({
    		type : 'post',
    		async : false,
    		url : url,
    		data : params,
    		dataType : 'json',
    		headers:headers,
    		success : function(data, status) {
    			result = data;
    			if(data&&data.code&&data.code=='101'){
    				modals.error("操作失败,请刷新重试,具体错误:"+data.message);
    				return false;
    			}
    			if (callback) { 
    				callback.call(this, data, status);
    			}
    		},
    		error : function(err, err1, err2) {
    		    if(err && err.readyState && err.readyState == '4'){
                    var responseBody = err.responseText;
                    if(responseBody){   
                    	 responseBody = "{'retData':"+responseBody;
                         var resJson = eval('(' + responseBody + ')');
                         $("#csrftoken").val(resJson.csrf.CSRFToken);
                         this.success(resJson.retData, 200);
                    }
                    return ;
                } 		    
    			modals.error({
    				text : JSON.stringify(err) + '<br/>err1:' + JSON.stringify(err1) + '<br/>err2:' + JSON.stringify(err2),
    				large : true
    			});
    		}
    	});
    
    	return result;
    }
    

      7、form表单配置

    <input type='hidden' value='${CSRFToken}' id='csrftoken'>
    

      8、注解定义

    package com.cnpc.framework.annotation;
    
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
     * 跨站请求仿照注解 刷新CSRFToken
     * 
     */
    @Target({ java.lang.annotation.ElementType.METHOD })
    @Retention(RetentionPolicy.RUNTIME)
    public @interface RefreshCSRFToken {
    
        /**
         * 刷新token
         *
         * @return
         */
        public abstract boolean refresh() default true;
    }
    

      9、注解配置

       @RefreshCSRFToken
        @RequestMapping(method = RequestMethod.GET, value = "/edit")
        private String edit(String id, HttpServletRequest request) {
    
            request.setAttribute("id", id);
            return "base/user/user_edit";
        }
    
        @RequestMapping(method = RequestMethod.POST, value = "/get")
        @ResponseBody
        private User getUser(String id) {
    
            return userService.get(User.class, id);
        }
    

      

  • 相关阅读:
    SharePoint 2013 配置基于表单的身份认证
    SharePoint 2013 场解决方案包含第三方程序集
    SharePoint 2010 站点附加数据升级到SP2013
    SharePoint 2013 在母版页中插入WebPart
    SharePoint 2013 搭建负载均衡(NLB)
    SharePoint 部署解决方案Feature ID冲突
    SharePoint 2013 配置基于AD的Form认证
    SharePoint Server 2016 Update
    SharePoint 2013 为用户组自定义EventReceiver
    SharePoint 2013 JavaScript API 记录
  • 原文地址:https://www.cnblogs.com/ailiying/p/7975983.html
Copyright © 2011-2022 走看看