zoukankan      html  css  js  c++  java
  • 防止跨站请求伪造(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)

  • 相关阅读:
    jQuery 删除元素
    jQuery 添加元素
    jQuery 捕获
    jQuery 链
    jQuery Callback
    jQuery 停止动画
    jQuery 动画
    jQuery 滑动
    jQuery 淡入淡出
    SP2010开发和VS2010专家"食谱"--第七章节--使用客户端对象模型
  • 原文地址:https://www.cnblogs.com/yangzhilong/p/4917840.html
Copyright © 2011-2022 走看看