`
郑云飞
  • 浏览: 794952 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Spring MVC 用拦截器+token防止重复提交

阅读更多

最近再开发一个图书管理的项目,在线发布图书的时候,明明只点击了一下,偏偏却保存了多条记录,无奈之下只好加拦截器防止重复提交:

1:首先定义注解:

首先自定义一个注解:

package com.dinfo.interceptor;

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 Token {

    boolean save() default false;

    boolean remove() default false;
}
2:接着实现一个拦截器借口:
package com.dinfo.interceptor;

import java.lang.reflect.Method;
import java.util.UUID;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

public class TokenInterceptor extends HandlerInterceptorAdapter {
    private static final Logger LOG = Logger.getLogger(Token.class);
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Method method = handlerMethod.getMethod();
            Token annotation = method.getAnnotation(Token.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());
                        return false;
                    }
                    request.getSession(true).removeAttribute("token");
                }
            }
            return true;
        } else {
            return super.preHandle(request, response, handler);
        }
    }

    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;
    }
}

 3: spring 拦截器配置:

<mvc:interceptors>
        <!-- 配置Token拦截器,防止用户重复提交数据 -->
        <mvc:interceptor>
            <mvc:mapping path="/**"/><!--这个地方时你要拦截得路径 我这个意思是拦截所有得URL-->
            <bean class="com.dinfo.interceptor.TokenInterceptor"/><!--class文件路径改成你自己写得拦截器路径!! -->
        </mvc:interceptor>
</mvc:interceptors>

 4:在跳转页面的方法加上  @Token(save=true)

@RequestMapping(value = "/webui/payPage.html", method = RequestMethod.GET)
	@Token(save=true)
	public ModelAndView gonext(HttpServletRequest request,
							   HttpServletResponse response, ModelMap model, String  id,int number) {
		ModelAndView mv=new ModelAndView();
		FmUtils.FmData(request,model);
		if(!StringUtils.isEmpty(id)) {
			TudouBookInfo bookInfo=bookService.findById(id);
			if(!StringUtils.isEmpty(bookInfo)){
				bookInfo.setNumber(number);
				float price=Float.parseFloat(bookInfo.getPrice());
				float totalPrice=number *price;
				bookInfo.setTotalPrice(totalPrice+"");
				mv.addObject("book",bookInfo);
			}
		}
		mv.setViewName(MobilePageContants.MY_PAY_PAGE);
		mv.addObject("token",id);
		return mv;
	}

 5:在提交的方法上加    @Token(remove=true)

@RequestMapping(value = "/webui/paysave.html", method = RequestMethod.POST)
	@Token(remove=true)
	public ModelAndView paysave(HttpServletRequest request,
								   HttpServletResponse response, ModelMap model, String  id,String number) {
		ModelAndView mv=new ModelAndView();

 

 6:OK,到这里大功告成,你就可以发现已经没有办法重复提交数据了!

 

 

分享到:
评论
3 楼 greatwqs 2018-08-21  
在前置请求页面返回token, 在提交接口中验证token, 并删除.
还是不错的方案.    
2 楼 zhukewen_java 2017-11-25  
这种方法的缺陷在于如果请求了两个save=true的方法,后面的请求的,会把session里的token override掉,比如
1,请求页面a
2,请求页面b
3,提交页面a(这里会失败)
4,   提交页面b
倒是可以在 session中放一个set,请求的时候判断token在不在这个set中(if (set.remove(token))
1 楼 byrgl5 2016-10-21  
请问如果重复提交了三次呢?能避免吗?

相关推荐

    spring-token:整合JWT,spring,springMVC,实现基于token验证

    因为这篇博客讲的是基于JWT和spring,springMVC的整合,所以如果有小伙伴想整合JWT和spring-boot,只需要将spring基于xml文件的拦截器配置方式转换成 spring-boot的配置方式就ok了。 欢迎大家讨论,分享!

    拦截器和控制器的区别

    4、拦截器可以利用依赖注入,因此在spring框架程序中,优先拦截器 5、拦截器是包裹在过滤器中使用的。 复习 converter 转换器 i18n struts2 spring MVC 拦截器 interceptor 过滤器 filter web.xml ...

    spring boot MVC

    spring boot 携带token登录,token拦截器,增删改查功能

    spring security 参考手册中文版

    5.1.3使用Spring MVC的AbstractSecurityWebApplicationInitializer 32 5.2 HttpSecurity 32 5.3 Java配置和表单登录 34 5.4授权请求 35 5.5处理注销 36 5.5.1 LogoutHandler 37 5.5.2 LogoutSuccessHandler 37 5.5.3...

    Express + Node.js实现登录拦截器的实例代码

    这边的拦截器,对应于spring MVC中的filter,所有的http请求,通过拦截器处理之后才能访问到对应的代码/资源。 最典型的应用场景就是实现访问权限控制,给予不同的用户/用户组不同的页面和接口访问权限,仅能够...

    Java SSM开发大众点评后端

    3Spring定时器、拦截器的应用 4跟踪Spring源码,追踪解决问题 前后端分离开发 手把手演示如何串联React技术栈App和管理后台 让后台Form表单提交符合RESTful规范 比较SPA和传统后台两种会话方式:Token和Session ...

    Struts2 in action中文版

    15.3 使用令牌防止表单重复提交 313 15.3.1 使用s:token/表单标签 313 15.3.2 令牌拦截器规则的例外 314 15.4 自动显示等待页面 316 15.5 完成CRUD操作的一个动作 317 15.5.1 CRUD 317 15.5.2 拦截器和接口 318 ...

    基于微服务的车票管理系统源码+项目说明+sql数据库.tar

    + SpringBoot构建后台管理系统,Redis+Token+MVC拦截器做权限校验 + 基于Nacos做服务和配置中心,共包含用户、订单、车次、事务、网关、验证码六个服务 + :star:利用OpenFeign做同步调用,利用RabbitMQ异步调用...

    低清版 大型门户网站是这样炼成的.pdf

    2.6.2 定义和使用拦截器栈 97 2.6.3 使用自定义拦截器 98 2.7 小结 100 第3章 struts 2标签库详解与ajax技术应用 103 3.1 struts 2主题与模板 103 3.2 struts 2控制标签详解 105 3.2.1 if/elseif/else标签详解...

    学生信息管理系统:大学生信息管理系统-初学路上自己摸索实践的项目

    添加CsrfInterceptor拦截器,对修改请求进行CsrfToken的校验,有效防止CSRF攻击对注册用户信息纠正服务层,强化在aop方法中对输入身份属性的校正,对“管理员”分支进行过滤,并引发异常,防止攻击者拦截请求JSON...

Global site tag (gtag.js) - Google Analytics