精华内容
下载资源
问答
  • 基于springboot实现表单重复提交.docx
  • 最近负责一个后台框架的搭建,使用的是springboot redis jwt 一直哐哐写,没有想到表单重复提交的问题,晚上想结构的时候突然想起来。 上代码, 引入依赖; 使用aop做拦截 <!-- SpringBoot 拦截器 --> <...

    最近负责一个后台框架的搭建,使用的是springboot redis jwt 一直哐哐写,没有想到表单重复提交的问题,晚上想结构的时候突然想起来。
    上代码,
    引入依赖;
    使用aop做拦截

       <!-- SpringBoot 拦截器 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-aop</artifactId>
            </dependency>
    	
            <!-- SpringBoot 集成redis -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-redis</artifactId>
            </dependency>
    
    

    自定义注解

    @Inherited
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface RepeatSubmit {
    }
    
    

    编写拦截

    @Aspect
    @Component
    public class NoRepeatSubmitAop {
    
        private static final Logger logger = LoggerFactory.getLogger(NoRepeatSubmitAop.class);
    
        @Autowired
        private RedisUtil redisUtil;
    	// 这个看自己的包结构 不然不做拦截
        @Around("execution(* com.jianfan.mdt.backend.project..*Controller.*(..))&& @annotation(nos)") 
        public Object arround(ProceedingJoinPoint pjp, RepeatSubmit nos) { //RepeatSubmit自己的写注解的名字
    
            try {
                ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
                String sessionId = RequestContextHolder.getRequestAttributes().getSessionId();
                HttpServletRequest request = attributes.getRequest();
                String key = sessionId + "-" + request.getServletPath();
                if(redisUtil.get(key)==null){
                    Object o = pjp.proceed();
                   redisUtil.set(key,key,5);//设置5秒内不能重复提交
                    return o;
                } else {
                    logger.error("重复提交");
                    return BaseResult.build(StatusCode.SERVICEERROR,StatusCode.REPAATMSG);
                }
    
            } catch (Throwable throwable) {
                logger.error("验证重复提交时出现未知异常!");
                return BaseResult.build(StatusCode.SERVICEERROR,StatusCode.REPAATMSGEXCEPT);
            }
    
        }
    
    }
    

    注解使用
    在这里插入图片描述
    测试结果
    在这里插入图片描述
    点赞加关注;免费分享众多开源项目;

    展开全文
  • SpringBoot防止表单重复提交(一) 一、重复提交的原因 用户表单登陆后直接转发到相应的页面,这是刷新页面会重复发送上次的请求,也就是会再次提交表单(浏览器会弹出表单重复提交的弹窗) 二、解决方法(存在漏洞) 在...

    SpringBoot防止表单重复提交(一)

    一、重复提交的原因

    用户表单登陆后直接转发到相应的页面,这是刷新页面会重复发送上次的请求,也就是会再次提交表单(浏览器会弹出表单重复提交的弹窗)
    

    二、解决方法(存在漏洞)

    1. 在后台登录成功后重定向到指定页面
    @PostMapping("/login")
    public String login(@RequestParam("name") String name, @RequestParam("pwd")String pwd, Map<String,Object> map){
        if (StringUtils.isEmpty(name) || StringUtils.isEmpty(pwd)){
            map.put("msg","error");
            return "login";
        }
        // 成功跳转到success后,如果刷新页面。会提示重新提交表单的请求
        // return "success";
        // 为了防止表单重复提交,这里将请求重定向到主页
        return "redirect:/main.html";
    }
    
    1. 追加映射
    @Bean
    public WebMvcConfigurer webMvcConfigurer(){
       return new WebMvcConfigurer() {
           // 页面跳转,映射
           @Override
           public void addViewControllers(ViewControllerRegistry registry) {
    			registry.addViewController("/main.html").setViewName("success");
           }
    }
    

    三、漏洞

    上面的方法存在漏洞,如果直接访问main.html则不需要登陆就能访问

    四、拦截器解决漏洞

    1. 自定义拦截器类
    /**
     * 自定义拦截器
     */
    public class MyIntercept implements HandlerInterceptor {
        /**
         *在业务处理器处理请求之前被调用
         */
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            Object username = request.getSession().getAttribute("username");
            if(username == null)
            {   // 未登录,返回转发到登录页面
                request.setAttribute("msg","先登录");
                request.getRequestDispatcher("/index.html").forward(request,response);
                return false;
            }
            return true;
        }
        /**
         *在业务处理器处理请求执行完成后,生成视图之前执行
         */
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    
        }
        /**
         *在DispatcherServlet完全处理完请求后被调用,可用于清理资源等。返回处理(已经渲染了页面)
         */
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    
        }
    }
    
    1. 注入拦截器
    @Bean
    public WebMvcConfigurer webMvcConfigurer(){
         return new WebMvcConfigurer() {
             // 页面跳转,映射
             @Override
             public void addViewControllers(ViewControllerRegistry registry) {
                 registry.addViewController("/").setViewName("login");
                 registry.addViewController("/index.html").setViewName("login");
                 registry.addViewController("/change.html").setViewName("login");
                 registry.addViewController("/main.html").setViewName("success");
             }
    
             // 拦截器
             @Override
             public void addInterceptors(InterceptorRegistry registry) {
                 // 注册拦截器
                 registry.addInterceptor(new MyIntercept())
                         // 设置拦截的请求
                         .addPathPatterns("/**")
                         // 设置不拦截的请求
                         // 静态资源不需要设置,springboot已经做好了处理
                         .excludePathPatterns("/index.html","/","/change.html","/login");
    
             }
    }
    
    展开全文
  • springboot 搞定重复提交(本地锁)

    千次阅读 2019-05-06 17:46:37
    在平时开发中,如果网速比较慢的情况下,用户提交表单后,发现服务器半天都没有响应,那么用户可能会以为是自己没有提交表单,就会再点击提交按钮重复提交表单,我们在开发中必须防止表单重复提交…. 重复提交 ...

    在平时开发中,如果网速比较慢的情况下,用户提交表单后,发现服务器半天都没有响应,那么用户可能会以为是自己没有提交表单,就会再点击提交按钮重复提交表单,我们在开发中必须防止表单重复提交….

    重复提交

    字面意思就是提交了很多次,比如同样的数据被插入到数据库多次。

    方法

    利用 自定义注解、Spring Aop、Guava Cache 实现表单防重复提交(不适用于分布式,后面会讲分布式方式…)

    导入依赖

    主要是第三个

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>21.0</version>
        </dependency>
    </dependencies>
    

    自定义注解

    package com.example.inteceptor;
    
    import java.lang.annotation.*;
    
    /**
     *
     * Created by Administrator on 2019/5/5.
     * 自定义 锁的注解
     */
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    public @interface LocalLock {
        String key() default "";
        /**
         * 过期时间
         * TODO 由于用的 guava 暂时就忽略这属性吧 集成 redis 需要用
         */
        int expire() default 5;
    }
    
    

    Lock 拦截器(AOP)

    首先通过 CacheBuilder.newBuilder() 构建出缓存对象,设置好过期时间;其目的就是为了防止因程序崩溃锁得不到释放(当然如果单机这种方式程序都炸了,锁早没了;但这不妨碍我们写好点)

    在具体的 interceptor() 方法上采用的是 Around(环绕增强) ,所有带 LocalLock 注解的都将被切面处理;

    如果想更为灵活,key 的生成规则可以定义成接口形式(可以参考:org.springframework.cache.interceptor.KeyGenerator),这里就偷个懒了;

    package com.example.inteceptor;
    
    
    import com.google.common.cache.Cache;
    import com.google.common.cache.CacheBuilder;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.reflect.MethodSignature;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.util.StringUtils;
    
    import java.lang.reflect.Method;
    import java.util.concurrent.TimeUnit;
    
    /**
     * Created by Administrator on 2019/5/5.
     * 本章先基于 本地缓存来做,后续讲解 redis 方案
     */
    @Aspect
    @Configuration
    public class LockMethodInterceptor {
        private static final Cache<String, Object> CACHES = CacheBuilder.newBuilder()
                // 最大缓存 100 个
                .maximumSize(1000)
                // 设置写缓存后 5 秒钟过期
                .expireAfterWrite(20, TimeUnit.SECONDS)
                .build();
    
        @Around("execution(public * *(..)) && @annotation(LocalLock)")
        public Object interceptor(ProceedingJoinPoint pjp) {
            MethodSignature signature = (MethodSignature) pjp.getSignature();
            Method method = signature.getMethod();
            LocalLock localLock = method.getAnnotation(LocalLock.class); //通过反射拿到注解对象
            String key = getKey(localLock.key(), pjp.getArgs()); // 控制台设置了 key=arg[0},所以只匹配了第一个参数
            if (!StringUtils.isEmpty(key)) {
                if (CACHES.getIfPresent(key) != null) {
                    throw new RuntimeException("请勿重复请求");
                }
                // 如果是第一次请求,就将 key 当前对象压入缓存中
                CACHES.put(key, key);
            }
            try {
                return pjp.proceed();
            } catch (Throwable throwable) {
                throw new RuntimeException("服务器异常");
            } finally {
                // TODO 为了演示效果,这里就不调用 CACHES.invalidate(key); 代码了
            }
        }
    
        /**
         * key 的生成策略,如果想灵活可以写成接口与实现类的方式(TODO 后续讲解)
         *
         * @param keyExpress 表达式
         * @param args       参数
         * @return 生成的key
         */
        private String getKey(String keyExpress, Object[] args) {
            for (int i = 0; i < args.length; i++) {
                keyExpress = keyExpress.replace("arg[" + i + "]", args[i].toString());
            }
            return keyExpress;
        }
    }
    
    

    控制层

    @LocalLock(key="arg[0]") //TODO 第一个参数作为key验证,此处还有疑问,key的写法是怎样的?
        @GetMapping("/localLock")
        @ResponseBody
        @ApiOperation(value="本地测试防止重复提交")
        public String query(@RequestParam String token,@RequestParam String name) {
            return "success - " + token + "-"+name;
        }
    

    测试

    当token重复时,提示不要重复提交。

    展开全文
  • springboot重复提交

    2021-07-08 18:22:45
    https://www.cnblogs.com/daleyzou/p/noSubmitRepeat.html

    https://www.cnblogs.com/daleyzou/p/noSubmitRepeat.html

    展开全文
  • throw new Exception("你提交的太频繁了,请稍后再试"); } } else { this.redisUtil.set(key, String.valueOf(now)); Object proceed = pjp.proceed(); return proceed; } } } }  
  • } 这样的话 就完美解决了表单重复提交 但是又衍生出了另外一个问题: 我在浏览器中输入链接指定main的地址的话 还是可以跳转的 问题来了 就要解决 当前最简单的解决办法就是 在访问控制层的之前 我先判断下 ...
  • 文章目录前言装备Core-Code新增注解@...SpringBoot防止表单重复提交。基于拦截器对带注解的请求进行拦截,处理。 后面总结一下为什么要如此使用。 应用场景: 使用浏览器后退按钮重复之前的操作...
  • 什么是表单重复提交 什么是表单重复提交 各位大哥好,首先介绍一下什么是表单重复提交.(原谅我中英文句点不分,是因为开发的时候把中文的符号也切换成了英文的)一个场景就是小弟我在实习的时候做的第一个前后端分离的...
  • 一、背景 二、token.java 三、拦截器 四、应用实例
  • springboot解决form表单重复提交方案

    千次阅读 2019-05-17 11:15:57
     在实际业务系统应用过程中,都会存在一个表单数据重复提交的问题。针对这个问题网上也存在N多种解决方案。  为节省选择时间,因此在这N多种方案中,我整理了自认为从根本上解决重复提交的问题的一个最优方案。 ...
  • 1.自定义注解@NoRepeatSubmit 标记所有Controller中提交的请求 2.通过AOP对所有标记了@NoRepeatSubmit 的方法进行拦截 3.在业务方法执行前,获取当前用户的token或者JSessionId+当前请求地址,作为一个唯一的key,去...
  • springboot使用redis防止表单重复提交 引入redis依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <...
  • springboot2.1+redis+拦截器 防止表单重复提交详细完整介绍,所用到的文件都上传了,下载即可使用。自己花了半天整理,并且测试通过,使用在实际项目中的,希望对每一个下载的朋友有帮助。
  • SpringBoot防止重复请求,重复表单提交超级简单的注解实现之四(终极版II)原文链接:[https://www.lskyf.com/post/213](https://www.lskyf.com/post/213)前言:防重复提交业务流程图如下1.简化DuplicateSubmitToken...
  • /** * @description 防止表单重复提交注解 */@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)@Documentedpublic @interface DuplicateSubmitToken { //保存重复提交标记 默认为需要保存 boolean ...
  • SpringBoot(16) 校验表单重复提交

    千次阅读 2019-11-27 16:33:30
    在某些情况下,由于网速慢,用户操作有误(连续点击两下提交按钮),页面卡顿等原因,可能会出现表单数据重复提交造成数据库保存多条重复数据。 存在如上问题可以交给前端解决,判断多长时间内不能再次点击保存按钮,...
  • * @description 防止表单重复提交注解 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @Documented public @interface DuplicateSubmitToken { //保存重复提交标记 默认为需要保存 ...
  • * @description 防止表单重复提交注解 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @Documented public @interface DuplicateSubmitToken { //保存重复提交标记 默认为需要保存 ...
  • 叙述 平时开发的项目中可能会出现下面这些情况: ... 由于网速等原因造成页面卡顿,用户重复刷新提交页面。 黑客或恶意用户使用postman等...这些情况都会导致表单重复提交,造成数据重复,增加服务器负载,严重...
  • * @description 防止表单重复提交拦截器 */ @Aspect @Component @Slf4j public class DuplicateSubmitAspect { public static final String DUPLICATE_TOKEN_KEY="duplicate_token_key"; @Pointcut(&...
  • 首先需要说明的是之前的防止重复提交是指:一次请求完成之前防止重复提交,当然扩展下就可以做到会话间防止重复提交,还可以扩展为某个时间段或者永久防止重复提交(这个我就不实现了),下面我来扩展一下相同会话...
  • springboot重复提交

    2020-12-02 14:21:57
    if (redisUtils.hasKey(RIH_AUTH_FORM_USER.getValue() + keyMd5)) { log.debug("表单重复提交"); InterceptorJsonUtils.printJson(response, "400", "表单重复提交"); return false; } boolean flag = redisUtils....
  • 1.自定义一个annotion注解,标记那些想防止重复提交的方法。 2.利用自定义的拦截器或者aop,拦截住刚刚标记的方法。 3.获取redis中是否存在相同的请求,如果存在且时间间隔极短,此次视为重复性请求。 4.方法末尾将...
  • Description: 自定义注解防止表单重复提交 * @Author: LiWT * @Date: 2021/6/24 */ @Inherited @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public &...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,445
精华内容 978
关键字:

springboot表单重复提交

spring 订阅