精华内容
下载资源
问答
  • java防重复提交
    2021-03-01 08:09:46

    前两种是利用javascript,后面一种是在使用Struts的情况下的参考实现:

    1、javascript ,设置一个变量,只允许提交一次。

    var checkSubmitFlg = false;

    function checkSubmit()

    {

    if (checkSubmitFlg == true)

    {

    return false;

    }

    checkSubmitFlg = true;

    return true;

    }

    document.ondblclick =

    function docondblclick()

    {

    window.event.returnValue = false;

    }

    document.onclick =

    function doconclick()

    {

    if (checkSubmitFlg)

    {

    window.event.returnValue = false;

    }

    }

    method="post" οnsubmit="return checkSubmit();">

    2、还是javascript,将提交按钮或者image置为disable

    method="post"

    οnsubmit="getElById('submitInput')

    .disabled = true;

    return true;

    ">

    styleId="submitInput"

    src="images/ok_b.gif"

    border="0" />

    3、利用struts的同步令牌机制

    利用同步令牌(Token)机制来解决Web应用中重复提交的问题,Struts也给出了一个参考实现。

    基本原理:

    服务器端在处理到达的请求之前,会将请求中包含的令牌值与保存在当前用户会话中的令牌值进行比较,看是否匹配。在处理完该请求后,且在答复发送给客户端之前,将会产生一个新的令牌,该令牌除传给客户端以外,也会将用户会话中保存的旧的令牌进行替换。这样如果用户回退到刚才的提交页面并再次提交的话,客户端传过来的令牌就和服务器端的令牌不一致,从而有效地防止了重复提交的发生。

    if (isTokenValid(request, true))

    {

    // your code here

    return mapping.findForward("success");

    } else

    {

    saveToken(request);

    return mapping.findForward

    ("submitagain");

    }

    Struts根据用户会话ID和当前系统时间来生成一个唯一(对于每个会话)令牌的,具体实现可以参考TokenProcessor类中的generateToken()方法。

    1. //验证事务控制令牌,会自动根据session中标识生成一个隐含input代表令牌,防止两次提交

    2. 在action中:

    //

    name="org.apache.struts.taglib.html.TOKEN"

    // value="6aa35341f25184fd996c4c918255c3ae">

    if (!isTokenValid(request))

    errors.add(ActionErrors.GLOBAL_ERROR,

    new ActionError("error.transaction.token"));

    resetToken(request);

    //删除session中的令牌

    3. action有这样的一个方法生成令牌

    protected String generateToken

    (HttpServletRequest request)

    {

    HttpSession session =

    request.getSession();

    try

    {

    byte id[] =

    session.getId().getBytes();

    byte now[] =

    new Long(System.currentTimeMillis()).

    toString().getBytes();

    MessageDigest md =

    MessageDigest.getInstance("MD5");

    md.update(id);

    md.update(now);

    return (toHex(md.digest()));

    } catch (IllegalStateException e)

    {

    return (null);

    } catch (NoSuchAlgorithmException e)

    {

    return (null);

    }

    }

    更多相关内容
  • Java怎样防止重复提交

    2020-12-22 17:08:54
    防止重复提交java解决  B/S结构的软件开发中,特别是在越大型的分布式应用中体现的越明显,后端的处理往往会因为出现较多的时间消耗而引起延迟,这种延迟有可能过长而终使用户认为是自己的操作错误,导致他们重新...
  • java防止重复提交

    2022-06-17 04:55:08
    java防止重复提交

    公司有个抽奖活动每人一天只能抽取一次,有用户恶意短时间内重复提交多次导致抽奖多发情况

    解决思路

    1.创建一个map集合存储每个用户对象作为对象锁,存储用户对象时要采用双重校验锁保证唯一性
    2.在控制层加同步代码块,不能在业务层加因为事务会导致同步代码块失效
    3.抽奖完成后进行把用户给移除掉释放内存

    1.工具类

    package com.yujie.utils;
    
    import com.yujie.model.User;
    
    import java.util.Map;
    import java.util.concurrent.ConcurrentHashMap;
    
    public class MapConcurrent {
        private static Map<String, User> userMap= new ConcurrentHashMap<>();
    
        public static User getUser(String name){
            //双重校验
            if (!userMap.containsKey(name)) {
                synchronized (MapConcurrent.class){
                    if (!userMap.containsKey(name)) {
                        User user = new User();
                        user.setUserName(name);
                        userMap.put(name,user);
                        return user;
                    }
                }
            }
            return userMap.get(name);
    
        }
    
        public static void removeUser(String name){
            userMap.remove(name);
        }
    }
    

    2.controller层,模拟用户抽奖代码

    
        @RequestMapping("/consume")
        public String consume(){
            //伪造前端数据
            User user1 = new User();
            user1.setUserName("王大宝");
            user1.setId(1);
            user1.setNum(1);
            User u = MapConcurrent.getUser(user1.getUserName());
            System.out.println("当前对象锁"+u.hashCode());
            //加锁并发安全
            synchronized (u){
                userService.consume(user1);
                //抽奖结束销毁用户对象
                MapConcurrent.removeUser(user1.getUserName());
            }
            return "抽奖成功";
        }

    2.service层代码

    @Service
    public class UserServiceImpl implements UserService {
        @Autowired
        private UserDao userDao;
        @Override
        @Transactional
        public  void consume(User user) {
            System.out.println(Thread.currentThread().getName());
            User user1 = selectUserById(user.getId());
            if(user1 != null){
                Integer num = user1.getNum();
                if(num != null && num>0){
                    user1.setNum(num-1);
                    userDao.save(user1);
                    System.err.println("100元话费卡-卡号:"+new Random().nextInt(99999999)+"密码:"+new Random().nextInt(99999999) +",剩余抽奖次数:"+(num-1));
                }
            }
        }
    

    以下是模拟用户10次并发提交抽奖

    3.没加锁前运行效果,导致一次抽奖发了10次奖品的bug

    当前对象锁1743829393
    当前对象锁1743829393
    当前对象锁1743829393
    当前对象锁1743829393
    当前对象锁1743829393
    当前对象锁1743829393
    当前对象锁1743829393
    当前对象锁1743829393
    当前对象锁1743829393
    当前对象锁1743829393
    http-nio-8080-exec-8
    http-nio-8080-exec-5
    http-nio-8080-exec-10
    http-nio-8080-exec-9
    http-nio-8080-exec-7
    http-nio-8080-exec-4
    http-nio-8080-exec-6
    http-nio-8080-exec-1
    http-nio-8080-exec-3
    http-nio-8080-exec-2
    100元话费卡-卡号:30879231密码:6585717,剩余抽奖次数:0
    100元话费卡-卡号:92273305密码:14406295,剩余抽奖次数:0
    100元话费卡-卡号:38463818密码:4953953,剩余抽奖次数:0
    100元话费卡-卡号:1038147密码:34559577,剩余抽奖次数:0
    100元话费卡-卡号:56007678密码:41413612,剩余抽奖次数:0
    100元话费卡-卡号:33740124密码:81815012,剩余抽奖次数:0
    100元话费卡-卡号:9332570密码:19397480,剩余抽奖次数:0
    100元话费卡-卡号:36659655密码:58524451,剩余抽奖次数:0
    100元话费卡-卡号:6957685密码:98985577,剩余抽奖次数:0
    100元话费卡-卡号:71413799密码:29059920,剩余抽奖次数:0

    4.加锁后运行效果

    当前对象锁2137528174
    当前对象锁2137528174
    当前对象锁2137528174
    当前对象锁2137528174
    当前对象锁2137528174
    当前对象锁2137528174
    当前对象锁2137528174
    当前对象锁2137528174
    当前对象锁2137528174
    当前对象锁2137528174
    http-nio-8080-exec-3
    100元话费卡-卡号:58114881密码:79931529,剩余抽奖次数:0
    http-nio-8080-exec-2
    http-nio-8080-exec-1
    http-nio-8080-exec-5
    http-nio-8080-exec-4
    http-nio-8080-exec-10
    http-nio-8080-exec-7
    http-nio-8080-exec-9
    http-nio-8080-exec-8
    http-nio-8080-exec-6

    方式二采用redis setnx解决

    package com.yujie.utils;
    import org.springframework.data.redis.core.RedisCallback;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.stereotype.Component;
    
    import javax.annotation.Resource;
    import java.util.Objects;
    
    /**
     * @ClassName RedisLockUtil
     * @Description 使用redis做锁
     * @Author Wangyujie
     * @Version V1.1.0
     */
    @Component
    public class RedisLockUtil {
    
        @Resource
        RedisTemplate<String, Object> redisTemplate;
    
    
        /**
         * 获取锁,true 则得到锁,false 已被锁定
         * @param lockName       锁名称
         * @param lockExoire     锁时间毫秒
         * @return
         */
        public Boolean getLock(String lockName, Integer lockExoire) {
            return (Boolean) redisTemplate.execute((RedisCallback<?>) connection -> {
                // 获取时间毫秒值
                long expireAt = System.currentTimeMillis() + lockExoire + 1;
                // 获取锁
                Boolean acquire = connection.setNX(lockName.getBytes(), String.valueOf(expireAt).getBytes());
                if (acquire) {
                    return true;
                } else {
                    byte[] bytes = connection.get(lockName.getBytes());
                    // 非空判断
                    if (Objects.nonNull(bytes) && bytes.length > 0) {
                        long expireTime = Long.parseLong(new String(bytes));
                        // 如果锁已经过期
                        if (expireTime < System.currentTimeMillis()) {
                            // 重新加锁,防止死锁
                            byte[] set = connection.getSet(lockName.getBytes(),
                                    String.valueOf(System.currentTimeMillis() + lockExoire + 1).getBytes());
                            return Long.parseLong(new String(set)) < System.currentTimeMillis();
                        }
                    }
                }
                return false;
            });
        }
    
        /**
         * 删除锁
         * @param lockName
         */
        public void delLock(String lockName) {
            redisTemplate.delete(lockName);
        }
    
        /**
         * 获取锁Key
         * @param prefix    前缀
         * @param name      名称
         * @return
         */
        public static String getFullKey(String prefix, String name) {
            return prefix + "_" + name;
        }
    
    }

    在service层使用

    @Service
    public class UserServiceImpl implements UserService {
        @Autowired
        private RedisLockUtil redisLockUtil;
        @Autowired
        private UserDao userDao;
        @Override
        @Transactional
        public  void consume(User user) {
            Boolean lock = redisLockUtil.getLock(user.getUserName(), 20000);
            if (lock){
                System.out.println(Thread.currentThread().getName());
                User user1 = selectUserById(user.getId());
                if(user1 != null){
                    Integer num = user1.getNum();
                    if(num != null && num>0){
                        user1.setNum(num-1);
                        userDao.save(user1);
                        System.err.println("100元话费卡-卡号:"+new Random().nextInt(99999999)+"密码:"+new Random().nextInt(99999999) +",剩余抽奖次数:"+(num-1));
                    }
                }
            }
        }
    }

    方式三:使用数据库的FOR UPDATE ,当查询这条数据的时候就上行锁,其他线程就查询不了

    查询语句加上FOR UPDATE
    SELECT * FROM t_user WHERE id=1 FOR UPDATE ;
    

    service层上加上事务注解即可

        @Override
        @Transactional
        public  void consume(User user) {
            System.out.println(Thread.currentThread().getName());
            User user1 = selectUserById(user.getId());
            if(user1 != null){
                    Integer num = user1.getNum();
                    if(num != null && num>0){
                        user1.setNum(num-1);
                        userDao.save(user1);
                        System.err.println("100元话费卡-卡号:"+new Random().nextInt(99999999)+"密码:"+new Random().nextInt(99999999) +",剩余抽奖次数:"+(num-1));
                    }
                }
        }

    展开全文
  • 如何避免表单重复提交,这篇文章就为大家详细介绍了Java表单重复提交的避免方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
  • java防重复提交

    2021-03-17 13:41:51
    防重复提交 简介: 客户端访问时,拦截访问数据,进行验证是否是配置的时间内(如下例子:ttl = 10),有相同的参数访问,如果有就相当于数据重复访问。不可以提交。 实例<一>: 后台代码 //防重复提交,表示...

    防重复提交

    简介:
    (1)客户端访问时,拦截访问数据,
    (2)进行验证是否是配置的时间内(如下例子:ttl = 10),有相同的参数访问,
    (3)如果有就相当于数据重复访问,直接返回。
    (4)以下两种方法供大家参考,每个人都有自己喜欢的方式去使用
    实例<一>:
    引入jia包:

    <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    

    后台代码

    	//防重复提交,表示10秒之内的不允许有相同的ps数据,也就是说10秒之内不允许有相同的ps = {"req.mobile"}
        @RequestMapping("/request")
        @Recommit(ttl = 10, ps = {"req.mobile"}, type = 2)
        public Result applyFranchiseStore(Req req) {
            try {
                return userMemberApplyService.applyIdentity(req);
            } catch (Exception e) {
                log.error("申请代理人或者加盟店异常" + req.toString(), e);
                return Result.buildFail("申请失败");
            }
        }
    

    配置重复提交

    package dorago.yiqiancms.biz.common.recommit;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.METHOD})
    public @interface Recommit {
    
        /**
         * 参与重复提交计算的请求参数
         * @return
         */
        String[] ps() default {};
    
        /**
         * 周期内参数相同的提交判定为重复提交
         * 单位: 秒
         * @return
         */
        int ttl() default 1;
    
        /**
         * 使用方区分:1.app 2.h5   如果不需要区分 此参数可以不要
         * @return
         */
        int type() default 1;
    
    }
    

    请求拦截,检查重复性

    package com.dorago.common.interceptor.recommit;
    
    import com.alibaba.fastjson.JSON;
    import com.dorago.common.Result;
    import com.dorago.common.SpringUtil;
    import com.dorago.common.interceptor.log.MethodLogAspect;
    import com.dorago.syj.biz.utils.RedisUtil;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.codec.digest.DigestUtils;
    import org.apache.commons.lang.StringUtils;
    import org.apache.commons.lang.math.NumberUtils;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Pointcut;
    import org.aspectj.lang.reflect.MethodSignature;
    import org.springframework.core.annotation.Order;
    import org.springframework.stereotype.Component;
    
    import javax.annotation.Resource;
    import java.lang.reflect.Method;
    import java.util.Arrays;
    
    @Aspect
    @Component
    @Slf4j
    @Order(1)
    public class RecommitAspect {
    
        @Resource
        RedisUtil redisUtil;
    
        @Pointcut("@annotation(com.dorago.common.interceptor.recommit.Recommit)")
        public void recommitPointcut(){}
    
        @Around("recommitPointcut()")
        public Object doAround(ProceedingJoinPoint jp) throws Throwable {
            boolean recommited = false;
            Object result;
            try {
                MethodSignature signature = (MethodSignature) jp.getSignature();
                Method m = signature.getMethod();
                Recommit rr = m.getAnnotation(Recommit.class);
                String ps = rr.ps();
                Object[] args = jp.getArgs();
                StringBuilder sb = new StringBuilder();
                if (args == null || args.length == 0) {
                    sb.append("NOARGS");
                } else if (StringUtils.isBlank(ps)) {
                    Arrays.stream(args).forEach(a->sb.append(JSON.toJSONString(a)).append("-"));
                } else {
                    String[] ns = ps.split(",");
                    if (ns.length > args.length) {
                        throw new Exception("ps param count invalid!");
                    }
                    for (String p : ns){
                        if(!NumberUtils.isDigits(p) || Integer.parseInt(p)>args.length){
                            throw new Exception("ps param value invalid!");
                        }
                        int idx = Integer.parseInt(p);
                        sb.append(JSON.toJSONString(args[idx-1])).append("-");
                    }
                }
                int ttl = rr.ttl();
                String key = sb.toString();
                String md5 = DigestUtils.md5Hex(key);
                String appName = SpringUtil.getProperty("spring.application.name");
                String cacheKey = "RR-" + appName + "-" + m.getDeclaringClass().getName() + "." + m.getName() + "-" + md5;
                //boolean exist = redisUtil.hasKey(cacheKey);
                recommited = !redisUtil.setIfAbsent(cacheKey, "", ttl);
            }catch (Exception e){
                log.error("RecommitAspect.doAround error:",e);
            }finally {
                if(!recommited) {
                    result = jp.proceed();
                }else{
                    result = Result.with(111, "重复提交");
                }
            }
            return result;
        }
    
    }
    

    其实我们使用下面一种方式也是可以的。
    实例<二>
    后台方法

        @Recommit(ttl=10) //此处这样写表示在10秒之内  如果有相同的参数orderId访问这个方法,那么就是重复提交的。
        public Result<Void> confirmNew(Long orderId) {
            String errMsg = null;
            Result<Void> result = Result.empty();
           //业务逻辑
           }
    

    提交配置

    package com.dorago.common.interceptor.recommit;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.METHOD})
    public @interface Recommit {
    
        /**
         * 参与重复提交计算的请求参数索引集合,逗号分隔
         * 例: 第1,2个参数则设置为"1,2",全部参数则用默认值即可
         * @return
         */
        String ps() default "";
    
        /**
         * 周期内参数相同的提交判定为重复提交
         * 单位: 秒
         * @return
         */
        int ttl() default 1;
    
    }
    

    请求拦截进行验证

    import com.alibaba.fastjson.JSON;
    import com.dorago.common.Result;
    import com.dorago.common.SpringUtil;
    import com.dorago.common.interceptor.log.MethodLogAspect;
    import com.dorago.syj.biz.utils.RedisUtil;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.codec.digest.DigestUtils;
    import org.apache.commons.lang.StringUtils;
    import org.apache.commons.lang.math.NumberUtils;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Pointcut;
    import org.aspectj.lang.reflect.MethodSignature;
    import org.springframework.core.annotation.Order;
    import org.springframework.stereotype.Component;
    
    import javax.annotation.Resource;
    import java.lang.reflect.Method;
    import java.util.Arrays;
    
    @Aspect
    @Component
    @Slf4j
    @Order(1)
    public class RecommitAspect {
    
        @Resource
        RedisUtil redisUtil;
        @Pointcut("@annotation(com.dorago.common.interceptor.recommit.Recommit)")
        public void recommitPointcut(){}
    
        @Around("recommitPointcut()")
        public Object doAround(ProceedingJoinPoint jp) throws Throwable {
            boolean recommited = false;
            Object result;
            try {
                MethodSignature signature = (MethodSignature) jp.getSignature();
                Method m = signature.getMethod();
                Recommit rr = m.getAnnotation(Recommit.class);
                String ps = rr.ps();
                Object[] args = jp.getArgs();
                StringBuilder sb = new StringBuilder();
                if (args == null || args.length == 0) {
                    sb.append("NOARGS");
                } else if (StringUtils.isBlank(ps)) {
                    Arrays.stream(args).forEach(a->sb.append(JSON.toJSONString(a)).append("-"));
                } else {
                    String[] ns = ps.split(",");
                    if (ns.length > args.length) {
                        throw new Exception("ps param count invalid!");
                    }
                    for (String p : ns){
                        if(!NumberUtils.isDigits(p) || Integer.parseInt(p)>args.length){
                            throw new Exception("ps param value invalid!");
                        }
                        int idx = Integer.parseInt(p);
                        sb.append(JSON.toJSONString(args[idx-1])).append("-");
                    }
                }
                int ttl = rr.ttl();
                String key = sb.toString();
                String md5 = DigestUtils.md5Hex(key);
                //获取配置文件的值
                String appName = SpringUtil.getProperty("spring.application.name");
                String cacheKey = "RR-" + appName + "-" + m.getDeclaringClass().getName() + "." + m.getName() + "-" + md5;
                //boolean exist = redisUtil.hasKey(cacheKey);
                recommited = !redisUtil.setIfAbsent(cacheKey, "", ttl);
            }catch (Exception e){
                log.error("RecommitAspect.doAround error:",e);
            }finally {
                if(!recommited) {
                    result = jp.proceed();
                }else{
                    result = Result.with(111, "重复提交");
                }
            }
            return result;
        }
    
    }
    

    总结:不管是1还是2的实例,主要就是配置Recommit的地方。配置好了之后,就在对应的方法上加上这个注解既可。

    展开全文
  • 主要介绍了JAVA防止重复提交Web表单的方法,涉及Java针对表单的相关处理技巧,具有一定参考借鉴价值,需要的朋友可以参考下
  • java防止重复提交解决方案

    千次阅读 2020-07-03 09:31:05
    java开发防止重复提交问题问题描述解决思路代码解释 问题描述 1.在我们项目开发过程中会出现用户保存操作时候快速点击两次会出现一条数据在数据库保存多条数据。 2.遇见上述问题我们首先跟前端开发沟通,在前端开发...

    java开发防止重复提交问题

    问题描述

    1.在我们项目开发过程中会出现用户保存操作时候快速点击两次会出现一条数据在数据库保存多条数据。

    2.遇见上述问题我们首先跟前端开发沟通,在前端开发过程中可以将操作按钮在操作完后就行置灰操作,虽然这样做了,但是不能从根们解决问题(前端和后端都防止,这样就可以根本解决问题),然后需要后端进行提交接口进行重复提交处理。

    解决思路

    1.前端利用js操作或者vue操作进行按钮置灰,防止二次点击!
    2.java后端利用redis进行防止重复操作!

    每次请求提交保存数据需要提前请求接口获取token,然后在提交请求将获取token放入需要提交数据的接口头部,然后将保存操作接口放入注解@ExtApiIdempotent(ConstantUtils.EXTAPIHEAD),后端会先进入aop中在头部找到token来判断redis是否存在当前redis,如果存在说明没有重复然后进行删除redis缓存数据,如果你连续点击第二次时候其实这个token已经被上一个请求给删除掉了,所以就是重复提交,不明白可以在下面评论,我都会回复

    代码解释

    1.在前端请求接口之前先请求获取一个token,我们java端将token生成完返给前端后,放入redis中,token作为key和token作为value 设置失效时间为200秒。

    @RequestMapping("/redisToken")
    public String getRedisToken() {
              return token.getToken();
      }
    

    这是token工具类

    package com.example.test.framework.redis;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    import org.springframework.util.StringUtils;
    
    import java.util.UUID;
    
    @Component
    public class RedisToken {
    
        @Autowired
        private BaseRedisService baseRedisService;
    
        @Autowired
        private RedisUtil redisUtil;
    
        /** 缓存指定时间200秒 */
        private static final long TOKENTIMEOUT = 200;
    
        /**
         * 生成Token
         */
        public String getToken(){
            String token = UUID.randomUUID().toString();
            token=token.replaceAll("-", "");
            // 将token放到Redis中,用UUID保证唯一性
            baseRedisService.setString(token, token, TOKENTIMEOUT);
            return token;
        }
    
        public synchronized boolean findToken(String tokenKey) {
            String tokenValue = (String) redisUtil.get(tokenKey);
            // 如果能够获取该(从redis获取令牌)令牌(将当前令牌删除掉) 就直接执行该访问的业务逻辑
            if (StringUtils.isEmpty(tokenValue)) {
                return false;
            }
            // 保证每个接口对应的token 只能访问一次,保证接口幂等性问题,用完直接删掉
            redisUtil.delete(tokenValue);
            return true;
        }
    }
    
    

    2.前端获取到token后,然后每次操作接口时候需要将token放入每次请求head中,后端需要利用AOP面向切面对请求token进行校验

    package com.example.test.framework.annotion;
    
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    /**
     * 带参数的,一般在前后端分离时候使用,就可以将token放入请求头部,就可以直接在接口处设置ConstantUtils.EXTAPIHEAD类型就可以了
     */
     //RetentionPolicy.RUNTIME:注解保留在程序运行期间,此时可以通过反射获得定义在某个类上的所有注解.
     //ElementType.METHOD:说明该注解只能被声明在一个类的方法前。
    @Target(value = ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface ExtApiIdempotent {
        //也就是接口中注解携带的参数,在我这里用的是请求方式类型 举例:@ExtApiIdempotent(ConstantUtils.EXTAPIHEAD)
        String value();
    }
    
    
    package com.example.test.framework.annotion;
    
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
     * 不带参数的一般在前后端没有分离,我们假如在controller跳转某个页面,然后在页面进行form表单提交,那就需要在跳转页面接口用@ExtApiToken这个注解,然后在页面用<input type="hidden" name="token" value="${token}">将token放入form标签内哦
     */
    @Target(value = ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface ExtApiToken {
    
    
    }
    
    
    package com.example.test.framework.aop;
    
    import com.example.test.framework.annotion.ExtApiIdempotent;
    import com.example.test.framework.annotion.ExtApiToken;
    import com.example.test.framework.redis.RedisToken;
    import com.example.test.framework.redisservice.ConstantUtils;
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    import org.aspectj.lang.reflect.MethodSignature;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    import org.springframework.util.StringUtils;
    import org.springframework.web.context.request.RequestContextHolder;
    import org.springframework.web.context.request.ServletRequestAttributes;
    
    import javax.servlet.http.HttpServletRequest;
    
    @Aspect
    @Component
    public class ExtApiAopIdempotent {
    
        @Autowired
        private RedisToken redisToken;
    
        // 1.使用AOP环绕通知拦截所有test下面某某文件下controller的方法
        // 2.这里是你需要拦截的controller位置,设置一个我们上层公共位置可以了,
        @Pointcut("execution(* com.example.test.*.controller.*.*(..)))")
        public void rlAop() {
    
        }
    
        /**
         * 封装数据-在请求连接中获取需要的参数,例如:请求头中token
         */
        public HttpServletRequest getRequest() {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
            return request;
        }
    
        /**
         * 前置通知-不带参数的一般在前后端没有分离,我们假如在controller跳转某个页面,然后在页面进行form表单提交,那就需要在跳转页面接口用@ExtApiToken这个注解,然后在页面用<input type="hidden" name="token" value="${token}">将token放入form标签内哦
         */
        @Before("rlAop()")
        public void before(JoinPoint point) {
            // 获取被增强的方法相关信息 - 查看方法上是否有次注解
    
            MethodSignature signature = (MethodSignature) point.getSignature();
            ExtApiToken extApiToken = signature.getMethod().getDeclaredAnnotation(ExtApiToken.class);
            if (extApiToken != null) {
                // 可以放入到AOP代码 前置通知
                getRequest().setAttribute("token", redisToken.getToken());
            }
        }
    
        /**
         * 环绕通知
         */
        @Around("rlAop()")
        public Object doBefore(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
            // 获取被增强的方法相关信息 - 查看方法上是否有次注解
            MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
            ExtApiIdempotent declaredAnnotation = methodSignature.getMethod().getDeclaredAnnotation(ExtApiIdempotent.class);
            if (declaredAnnotation != null) {
                String values = declaredAnnotation.value();
                String token = null;
                HttpServletRequest request = getRequest();
                if (values.equals(ConstantUtils.EXTAPIHEAD)) {
                    token = request.getHeader("token");
                } else {
                    token = request.getParameter("token");
                }
    
                // 获取不到token
                if (StringUtils.isEmpty(token)) {
                    return "获取不到token";
                }
    
                // 接口获取对应的令牌,如果能够获取该(从redis获取令牌)令牌(将当前令牌删除掉) 就直接执行该访问的业务逻辑
                boolean isToken = redisToken.findToken(token);
                // 接口获取对应的令牌,如果获取不到该令牌 直接返回请勿重复提交
                if (!isToken) {
                    return "请勿重复提交!";
                }
            }
            Object proceed = proceedingJoinPoint.proceed();
            return proceed;
        }
    }
    
    

    设置请求属于哪种类型可以是http-from两种请求

    package com.example.test.framework.redisservice;
    
    public interface ConstantUtils {
    
        /**
         * http 中携带的请求
         */
        static final String EXTAPIHEAD = "head";
        /**
         * from 中提交的请求
         */
        static final String EXTAPIFROM = "from";
    }
    
    

    controller层代码-如果你不是from表单提交就不要考虑ConstantUtils.EXTAPIFROM参数

    package com.example.test.systemmo.controller;
    
    
    import com.example.test.framework.annotion.ExtApiIdempotent;
    import com.example.test.framework.annotion.ExtApiToken;
    import com.example.test.framework.redis.RedisToken;
    import com.example.test.framework.redisservice.ConstantUtils;
    import com.example.test.systemmo.pojo.SysLcfLog;
    import com.example.test.systemmo.service.imp.SysLcfLogService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    import org.springframework.web.bind.annotation.RestController;
    
    import javax.servlet.http.HttpServletRequest;
    import java.lang.reflect.Array;
    import java.util.ArrayList;
    
    /**
     * <p>
     * 操作日志 前端控制器
     * </p>
     *
     * @author lcf
     * @since 2020-04-24
     */
    @RestController
    @RequestMapping("/systemmo/sss")
    public class SysLcfLogController {
            @Autowired
            private SysLcfLogService syslcflogservice;
    
            @Autowired
            private RedisToken token;
    
            @RequestMapping("/redisToken")
            public String getRedisToken() {
                return token.getToken();
            }
    
            /**
             * http请求增加日志
             * @param syslsflog
             * @return
             */
            @RequestMapping("/add")
            @ExtApiIdempotent(ConstantUtils.EXTAPIHEAD)
            public Object add(SysLcfLog syslsflog){
                //你的业务逻辑操作
             return "ok";
            }
    
    
            /**
             * 跳转页面,我们需要将token给你在跳转时候传送给你的页面,这样就可以在form表单提交时候直接传入给fromAdd接口
             * @param req
             * @return
             */
            @RequestMapping("/logPage")
            @ExtApiToken
            public String logPage(HttpServletRequest req) {
                return "logPage";
            }
    
    
    
        /**
         * form增加日志
         * @param syslsflog
         * @return
         */
        @RequestMapping("/fromAdd")
        @ExtApiIdempotent(ConstantUtils.EXTAPIFROM)
        public Object fromAdd(SysLcfLog syslsflog){
            //你的业务逻辑操作
            return "ok";
        }
    
            @RequestMapping("/select")
            public Object select(){
                ArrayList<SysLcfLog> log=new ArrayList<SysLcfLog>();
                log=syslcflogservice.select();
                return log;
            }
    }
    
    

    防止重复提交进行根据http请求和from表单提交进行总结分析,遇见问题时候也是在网上找资料,总结成自己的语言分享给大,大家哪里不懂,我会一一回复,后期会更新更多硬货,都是在实战项目中遇见的

    展开全文
  • java防止前端重复提交

    2022-04-07 15:46:58
    JAVA利用自定义本地锁解决重复提交的问题 引入jar包 <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>31.1-jre</...
  • Java实现防重复提交

    千次阅读 2020-07-04 15:19:44
    防重复提交的重要性? 在业务开发中,为什么我们要去想办法解决重复提交这一问题发生?网上的概念很多:导致表单重复提交,造成数据重复,增加服务器负载,严重甚至会造成服务器宕机,那么为什么会造成这种现象?...
  • java防重复提交AOP

    2021-06-06 19:20:42
    使用时标记在controller需要防重复提交的类或者方法上; 2.定义AOP切面NoRepeatSubmitAspect切NoRepeatSubmit注解; 3.对请求方法query参数和body参数分别做MD5,作为redis中的key; 4.如果在redis中找到了就算重复...
  • 干货实战~Java如何防止接口重复提交

    千次阅读 2021-02-12 22:01:19
    正如本文标题所言,今天我们来聊一聊在Java应用系统中如何防止接口重复提交;简单地讲,这其实就是“重复提交”的话题,本文将从以下几个部分展开介绍:“重复提交”简介与造成的后果“防止接口重复提交”的实现思路...
  • Java开发中怎么防止重复提交

    千次阅读 2021-06-15 13:20:34
    这能避免用户按F5导致的重复提交,而其也不会出现浏览器表单重复提交的警告,也能消除按浏览器前进和后退按导致的同样问题。 3)在session中存放一个特殊标志 在服务器端,生成一个唯一的标识符,将它存入session,...
  • Java中如何避免重复提交请求

    千次阅读 2021-03-06 02:23:33
    查看后台日志后发现一个同样的请求提交了多次,后果就是轻则导致产生多条...二、产生原因导致重复请求的原因很多,大体为以下几种:多次点击提交按钮反复刷新页面点击浏览器后退按钮,导致重复提交表单浏览器重复的H...
  • java+redis+lua实现重复提交操作拦截.防止出现同一数据在同一时间被操作多次。实现基于aop和注解。
  • 主要介绍了Java后台防止客户端重复请求、提交表单实现原理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • 防止页面重复提交demo

    2018-07-12 15:22:56
    通过前端控制和后台session存储的随机token防止页面重复提交
  • 防止表单重复提交,或者是防止按F5 刷新提交表单。在WEB开发中是经常会碰到这样的问题的。目前主流的解决方法有以下三种:1、采用脚本来解决2、重定向到别的页面3、使用s:token 标签由于我是使用S2SH来开发的,所以...
  • 对于重复提交的问题,主要由于重复点击或者网络重发请求, 我要先了解产生原因几种方式:对于重复提交的问题 主要涉及到时 问题,那么先说一下什么是幂等。 幂等:F(F(X)) = F(X)多次运算结果一致;简单点说就是对于...
  • Java防止相同数据重复提交方案

    千次阅读 2020-06-08 23:35:07
    Java防止相同数据重复提交方案场景解决办法 场景 在开发工作中由于接口处理数据过慢,导致还没保存到数据库时再次提交相同的数据,此时数据库内没有这条数据出现了重复提交的脏数据 解决办法 1.前端控制提交未返回结果...
  • java后台防重复提交

    2021-11-18 14:03:29
    import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.METHOD, Element...
  • 我们大家再进行web开发的时候,必不可少会遇见表单重复提交问题。来给总结如何解决表单提交问题,欢迎大家交流指正。  首先我们在讨论如何解决表单重复提交问题之前先来解决三个问题:1.什么叫表单重复提交?2.什么...
  • 利用redis和aop实现防止接口重复请求
  • JAVA AOP防止重复提交

    2021-01-13 16:12:45
    通过Aop进行防止重复提交, 把pds-sessionId-请求路径作为请求的唯一key,用户发送请求时,判断key是否存在,如果存在则重复提交,不存在,则进行保存,执行完后或者出现异常进行清除。 key 超时5秒自动清除 一....
  • 如果是,那就返回continue给slave,否则需要进行全量复制(因为这说明已经错过了很多数据了) **「⑥」**master发送从slave的offset开始到缓冲区队列结尾的数据给slave 1200页Java架构面试专题及答案 小编整理不易,...
  • java实现防止表单重复提交

    热门讨论 2010-07-19 10:46:14
    服务器端避免表单的重复提交,利用同步令牌来解决重复提交的基本原理如下:(1)用户访问提交数据的页面,服务器端在这次会话中,创建一个session对象,并产生一个令牌值,将这个令牌值作为隐藏输入域的值,随表单一起发送到...
  • 用户在操作表单Post数据时往往会出现表单数据重复提交的问题,尤其在Web开发中此类问题比较常见。刷新页面,后退操作以前的页面,单机多次按钮都会导致数据重复提交。此类问题是因为浏览器重复提交HTTP请求导致。...
  • Java后端防止频繁请求、重复提交

    千次阅读 热门讨论 2022-04-10 15:29:21
    Java后端防止频繁请求、重复提交 在客户端网络慢或者服务器响应慢时,用户有时是会频繁刷新页面或重复提交表单的,这样是会给服务器造成不小的负担的,同时在添加数据时有可能造成不必要的麻烦。所以我们在后端也有...
  • 传统方式(不推荐)首先我们介绍下之前传统的防重复提交方式:1:前端处理:思路如下:function dosubmit(){//第一步,我们需要获取表单的提交按钮。var btnSubmit = document.getElementById("submit");//第二步,...
  • 方案一:利用Session防止表单重复提交 方案二:判断请求url的数据是否和上一次相同 方案三:利用Spring AOP 和redis的锁来实现防止表单重复提交 ....

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 79,797
精华内容 31,918
关键字:

java防重复提交