防重复提交之传递token方法

防重复提交是一件很普通的事,因为之前这块只拿来用了.并未关心其中的实现,现在回来记录一下.

实现方式是:前端在需要防重复提交的页面进入时去服务器拿到token,服务器要把token放到session中一份,等前端提交时删除token,第二次提交时发现token没有.就直接返回重复提交就行了.这种局限性很大,不适合微服务,微服务的话我理解需要用数据库唯一索引(不推荐)或者redis锁(推荐)来实现.

首先编写一个自定义注解RepeatToken

package com.hex.boot.hello.controller;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RepeatToken {

    boolean save() default false ;
    boolean remove() default false ;
}

controller实现

@Controller
@Slf4j
public class HelloController {
    /**需要获取token的接口
     * @date 2019/12/13
     * @param
     * @return
     * @author hex
     */
    @RepeatToken(save = true)
    @RequestMapping("/addToken")
    public String add(HttpServletRequest request) {
       log.info("token:"+request.getSession(true).getAttribute("token"));
        return "hello";
    }

    /**
     * 调用防止重复提交的接口
     * @param request
     * @param token
     * @return
     */
    @RepeatToken(remove = true)
    @RequestMapping("/removeToken")
    public String removeToken(HttpServletRequest request, @RequestParam(value = "token") String token) {
       log.info("token:"+token);
        return "hello";
    }
}

编写token拦截器

package com.hex.boot.hello.interceptor;

import com.hex.boot.hello.controller.RepeatToken;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
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.lang.reflect.Method;
import java.util.UUID;
/**
 * @Date 2019/12/13 9:22
 * @Author hex
 * @Desc
 */
@Slf4j
@Component
public class TokenInterceptor extends HandlerInterceptorAdapter {
    //private static final Logger LOG = LoggerF.getLogger(TokenInterceptor.class);

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info("进入token拦截器!");
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Method method = handlerMethod.getMethod();
            RepeatToken annotation = method.getAnnotation(RepeatToken.class);
            if (annotation != null) {
                boolean needSaveSession = annotation.save();
                if (needSaveSession) {
                    request.getSession(true).setAttribute("token", UUID.randomUUID().toString());
                }
                boolean needRemoveSession = annotation.remove();
                if (needRemoveSession) {
                    if (isRepeatSubmit(request)) {
                        log.warn("please don't repeat submit,url:" + request.getServletPath());
                        //如果重复提交,则重定向到列表页面
                        response.sendRedirect("http://www.baidu.com");
                        return false;
                    }
                    request.getSession(true).removeAttribute("token");
                }
            }
            return true;
        } else {
            return super.preHandle(request, response, handler);
        }
    }

    /***
     *
     * @param request
     * @return : true:报错需要重定向 <br />
     * false: 处理后续的正常业务逻辑
     */
    private boolean isRepeatSubmit(HttpServletRequest request) {
        String serverToken = (String) request.getSession(true).getAttribute("token");
        if (serverToken == null) {
            return true;
        }
        String clinetToken = request.getParameter("token");
        if (clinetToken == null) {
            return true;
        }
        if (!serverToken.equals(clinetToken)) {
            return true;
        }
        return false;
    }
}

对于非springboot项目直接在springmvc配置下拦截器即可,对于springboot项目需要将拦截器手动加进来:

package com.hex.boot.hello.interceptor;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * @Date 2019/12/13 9:35
 * @Author hex
 * @Desc
 */
@Configuration
public class TokenConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //注册TestInterceptor拦截器
        InterceptorRegistration registration = registry.addInterceptor(new TokenInterceptor());
        //所有路径都被拦截
        registration.addPathPatterns("/**");
        //添加不拦截路径
        registration.excludePathPatterns(
              
        );
    }
}
发布了55 篇原创文章 · 获赞 17 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/he_xiao123/article/details/103521263