AOP+Token防止表单重复提交

表单重复提交:

由于用户误操作,多次点击表单提交按钮

由于网速等原因造成页面卡顿,用户重复刷新提交页面

 

避免表单重复提交的方式:

1.页面上的按钮做防重复点击操作

2.在数据库中可以做唯一约束

3.利用token校验重复提交

 

如何利用token校验表单重复提交

思路:在表单提交前先请求后台获取token,后台随机生成token保存在session中,提交表单时在请求参数中带上获取的token即可,后台校验token是否匹配。

token的获取和校验可以统一写在AOP切面类中。

 

自定义注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Token {
    boolean save() default false ;
    boolean remove() default false ;
}

 

切面类

@Aspect
@Component
public class TokenAspect {

    @SuppressWarnings("unused")
    @Before("within(@org.springframework.stereotype.Controller *) && @annotation(token)")
    public void testToken(final JoinPoint joinPoint, Token token){
        try {
            if (token != null) {
                //获取 joinPoint 的全部参数
                Object[] args = joinPoint.getArgs();
                HttpServletRequest request = null;
                HttpServletResponse response = null;
                for (int i = 0; i < args.length; i++) {
                    //获得参数中的 request && response
                    if (args[i] instanceof HttpServletRequest) {
                        request = (HttpServletRequest) args[i];
                    }
                    if (args[i] instanceof HttpServletResponse) {
                        response = (HttpServletResponse) args[i];
                    }
                }

                boolean needSaveSession = token.save();
                if (needSaveSession){
                    String uuid = UUID.randomUUID().toString();
                    request.getSession().setAttribute( "token" , uuid);
                    System.out.println("进入表单页面,Token值为:"+uuid);
                }

                boolean needRemoveSession = token.remove();
                if (needRemoveSession) {
                    if (isRepeatSubmit(request)) {
                        System.out.println("表单重复提交");
                        throw new FormRepeatException("表单重复提交");
                    }
                    request.getSession(false).removeAttribute( "token" );
                }
            }

        } catch (FormRepeatException e){
            throw e;
        } catch (Exception e){
            e.printStackTrace();
            throw e;
        }
    }

    private boolean isRepeatSubmit(HttpServletRequest request) throws FormRepeatException {
        String serverToken = (String) request.getSession( false ).getAttribute( "token" );
        if (serverToken == null ) {
            return true;
        }
        String clinetToken = request.getParameter( "token" );
        if (clinetToken == null || clinetToken.equals("")) {
            return true;
        }
        if (!serverToken.equals(clinetToken)) {
            return true ;
        }
        System.out.println("校验是否重复提交:表单页面Token值为:"+clinetToken + ",Session中的Token值为:"+serverToken);
        return false ;
    }
}

 

全局异常处理

@ControllerAdvice
public class ControllerAdviceHandler {
    
    @ResponseBody
    @ExceptionHandler(value={com.irish.exception.FormRepeatException.class})
    public String arithmeticExceptionHandler(Exception e){
           return "您重复提交表单了!";
    }

}

 

自定义异常

public class FormRepeatException extends RuntimeException {

    private static final long serialVersionUID = 1L;

    public FormRepeatException(String message){ super(message);}
    
    
}

 

controller层

@Controller
public class URLController {

    
    /**
     * 获取token,并将token保存在session中
     * @return
     */
    @Token(save = true)
    @RequestMapping("/queryToken")
    @ResponseBody
    public String getToken(HttpServletRequest request, HttpServletResponse response){
        return (String) request.getSession().getAttribute("token");
    }

    /**
     * 提交表单的地址,在AOP中检查表单是否重复提交,将token删除
     * @param request
     * @param response
     * @return
     */
    @Token(remove = true)
    @RequestMapping("/submitFrom")
    @ResponseBody
    public String removeToken(HttpServletRequest request, HttpServletResponse response){
        return "success";
    }
}

 

项目结构:

 github下载地址:https://github.com/jake1263/form-repeat-submit

posted @ 2019-06-26 10:12  踏月而来  阅读(1161)  评论(0编辑  收藏  举报