• springmvc 用拦截器+token防止重复提交


    一,原理:

    1,在进入到提交页面时,使用拦截器拦截在进入此方法前,生成一个token,放到session中,

        @RequestMapping(value = "/{id}/details")
        @FormToken(produce = true)
        public Object details(@PathVariable String id, HttpServletRequest request){
            Map<String,Object> map = new HashMap<>();
            GameInfo gameInfo;
            int count;
            try {
                gameInfo = gameInfoService.get(id, -1);
                // 使用此游戏的活动数
                count = activityService.getCountByGid(id);
                gameInfo.setCount(count);
            } catch (TException e) {
                LOGGER.debug("获取指定游戏详情失败!!", e);
                return new MessageEntity.Builder(request).msg("跳转预览页面失败").code(201).success(false)
                        .create();
            }
            map.put("gameInfo",gameInfo);
            return new MessageEntity.Builder(request).msg("success").code(201).content(map).success(true)
                    .create();
        }

    @FormToken此标签:

    package com.caobug.client.annotation;
    
    import java.lang.annotation.*;
    
    /**
     * TOKEN生成与删除
     * Created by caobug on 14-8-15.
     */
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface FormToken {
    
        /**
         * 是否在页面生成TOKEN
         *
         * @return
         */
        boolean produce() default false;
    
        /**
         * 是否删除旧 TOKEN
         *
         * @return
         */
        boolean remove() default false;
    }

    实现一个拦截器接口

    package com.caobug.client.support.interceptor;
    
    import com.caobug.client.annotation.FormToken;
    import com.caobug.client.support.MessageCode;
    import com.caobug.client.support.MessageEntity;
    import com.caobug.client.support.utils.TokenUtils;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.http.MediaType;
    import org.springframework.web.bind.annotation.ResponseBody;
    import org.springframework.web.method.HandlerMethod;
    import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.PrintWriter;
    import java.lang.reflect.Method;
    
    /**
     * Token 拦截器
     * <p/>
     * Created by caobug on 14-8-15.
     */
    public class FormTokenInterceptor extends HandlerInterceptorAdapter {
    
        public final static String TOKEN_NAME = "resubmitToken";
    
        /**
         * 方法处理前处理
         *
         * @param request
         * @param response
         * @param handler
         * @return
         * @throws Exception
         */
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
                throws Exception {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Method method = handlerMethod.getMethod();
            FormToken formToken = method.getAnnotation(FormToken.class);
            if (null != formToken) {
                if ((formToken.produce() && formToken.remove()) || (!formToken.produce() && !formToken.remove())) {
                    throw new RuntimeException("请不要在同一个方法上同时注解:@FormToken(remove = true/false, produce = true/false)");
                } else if (formToken.produce()) {
                    request.getSession().setAttribute(TOKEN_NAME, TokenUtils.getToken());
                } else if (formToken.remove()) {
                    String serverToken = (String) request.getSession().getAttribute(TOKEN_NAME);
                    String clientToken = request.getParameter(TOKEN_NAME);
                    request.getSession().removeAttribute(TOKEN_NAME); // remove token
                    if (!StringUtils.equals(serverToken, clientToken)) {
                        if (null != method.getAnnotation(ResponseBody.class)) { // JSON
                            response.setContentType(MediaType.APPLICATION_JSON_VALUE);
                            PrintWriter out = response.getWriter();
                            out.print(new ObjectMapper().writeValueAsString(new MessageEntity.Builder(null).
                                    code(MessageCode.SEND_MULTIPLE).msg("无效请求,请刷新页面后重试").create()));
                            out.flush();
                            out.close();
                        } else { // 普通页面
                            request.getRequestDispatcher("/error/invalidRequest").forward(request, response);
                        }
                        return false;
                    }
                }
            }
            return super.preHandle(request, response, handler);
        }
    }

    xml配置:

    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
            <property name="interceptors">
                <list>
                    <bean class="com.caobug.client.support.interceptor.FormTokenInterceptor"/>
                </list>
            </property>
        </bean>

    2,在提交页面中接收到这个值:

    <input type="hidden" name="token" value="${token}" />

    3,提交处理;

    @RequestMapping("/SaveDataController/saveData")
        @ResponseBody
        @FormToken(remove=true)
        public void saveData(HttpServletRequest request,HttpServletResponse response,
                             String tablename,String trowkey,String columns,
                             String indextablename,String irowkey,String icolumns,
                             String task_id,String savetype,String memoryCodes){
            System.out.println(task_id);
            saveDataService.saveData(task_id,savetype,memoryCodes,tablename, trowkey, columns, indextablename, irowkey, icolumns);
        }

    4,第一次提交时,在还没进入到提交页面时,就在服务器端生成一个token(拦截器做出此动作),把此token传递给提交页面,点击“提交”按钮,进入到处理的method方法,拦截此方法,在拦截器中把session的值清空,对比两个token,

    http://www.cnblogs.com/hdwpdx/archive/2016/03/29/5333943.html

  • 相关阅读:
    SQLSERVER跨库访问
    Mybatis开发的几个主要事项
    jqGrid参数
    WPF 从当前层次遍历查找 子控件及父控件
    c# 获取图像像素
    异步FIFO的FPGA实现
    note5文档流
    note3css 的padding属性
    note3clip:rect('top', 'right', 'bottom', 'left')是什么意思
    怎样查看端口被占用情况
  • 原文地址:https://www.cnblogs.com/Eddyer/p/6062835.html
Copyright © 2020-2023  润新知