当前位置:   article > 正文

springboot防重注解_repeatsubmit注解

repeatsubmit注解

防重注解

使用形式

@NoRepeatSubmit(checkType = NoRepeatSumbitCheckType.PARAMS,checkRequestParams = {"info.name"})
@PostMapping("/testNoRepectSumbit")
public ResultDto testNoRepectSumbit(@RequestBody TestPerson testPerson){
    System.out.println("====testNoRepectSumbit");
    System.out.println("【map】 " + testPerson);
    return ResultDto.createSuccess("接口成功");
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

NoRepeatSubmit

注解配置信息

package cn.com.yusys.config;

import java.lang.annotation.*;

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

    long timeOut() default 1 ;

    //如果type为params,需要在checkRequestParams中设置防重使用的参数名
    NoRepeatSumbitCheckType checkType() default NoRepeatSumbitCheckType.IP_PATH;

    //如果参数是在多级对象里,如 paramOne:{paramSecond:secondValue}
    //格式化:paramOne.ParamSecond
    String[] checkRequestParams() default {};

    RequestType requestType() default RequestType.POST;
}


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

NoRepeatSumbitCheckType

枚举配置信息

package cn.com.yusys.config;

public enum  NoRepeatSumbitCheckType {
    //防重检测会对 token#ip 进行分割,分割符为 “#”
    TOKEN_IP_PATH_PARAMS("ip#path#params#token"),

    IP_PATH_PARAMS("ip#path#params"),

    IP_PATH("ip#path"),

    PARAMS("params");

    private String name;

    NoRepeatSumbitCheckType(String name){
        this.name = name;
    }

    public String getName(){
        return this.name;
    }
}


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

RequestType

请求方式配置

package cn.com.yusys.config;

public enum  RequestType {
    POST,
    GET;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

AspectConfig

切点配置信息

package com.tianqiauto.tis.api.xdd.noRepeatSubmit;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.tianqiauto.base.core.ErrorCode;
import com.tianqiauto.base.core.Result;
import com.tianqiauto.base.core.ResultGenerator;
import com.tianqiauto.base.model.AjaxResult;
import lombok.extern.slf4j.Slf4j;
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.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;


/**
 * @Description 防止重复提交
 * @Author wjx
 * @Date 2023/6/16 15:00
 **/
@Slf4j
@Aspect
@Component
public class AspectConfig {


    private static final String NO_REPEAT_HEADER = "NO_REPEAT:";

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    /**
     * @param point
     * @方法描述:通过检测注解 NoRepeatSubmit防止重复提交
     */
    @Around("@annotation(NoRepeatSubmit)")
    public Object NoRepeatSubmit(ProceedingJoinPoint point) throws Throwable {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        //获取注解中的防重间隔时间
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        NoRepeatSubmit annotation = method.getAnnotation(NoRepeatSubmit.class);
        long timeOut = annotation.timeOut();
        String checkTypeName = annotation.requestType().name();
        log.debug("【防重间隔时间 单位:秒】" + timeOut);
        //非checkTypeName(例如:POST)请求提示错误
        String requestTypeName = request.getMethod();
        if (!checkTypeName.equals(requestTypeName)) {
            throw new RuntimeException("【请求方式不正确】【requestTypeName:" + requestTypeName + "】【checkTypeName:" + checkTypeName + "】");
        }
        Class<?> returnType = method.getReturnType();
        //获取key
        String key = null;
        try {
            key = getKey(point, annotation, request);
        } catch (Exception e) {
            log.error("【防重注解获取key解析失败】失败原因:{}",e);
            return getFailResult(returnType);
        }
        log.debug("【防重主键】" + key);
        //判断redis中是否存在key
        Boolean notRepeat = redisTemplate.opsForValue().setIfAbsent(key, "1", timeOut, TimeUnit.SECONDS);
        if (!notRepeat) {
            log.warn("【重复提交】key:{}", key);
            return getFailResult(returnType);
        }
        return point.proceed();
    }

    private Object getFailResult(Class<?> returnType) {
        Object obj;
        if (returnType == Result.class) {
            obj = ResultGenerator.genFailResult(ErrorCode.NO_REPEAT_SUBMIT);
        } else {
            obj = AjaxResult.getInstance().setSuccessChain(false).setMessageChain(ErrorCode.NO_REPEAT_SUBMIT.getMessage());
        }
        return obj;
    }

    /**
     * @param point
     * @param annotation
     * @param request
     * @return
     * @方法描述:解析注解中的枚举配置信息
     */
    private String getKey(ProceedingJoinPoint point, NoRepeatSubmit annotation, HttpServletRequest request) {
        MethodSignature signature = (MethodSignature) point.getSignature();
        NoRepeatSumbitCheckType noRepeatSumbitCheckType = annotation.checkType();
        String name = noRepeatSumbitCheckType.getName();
        String[] splitStr = name.split("#");
        StringBuilder key = new StringBuilder();
        key.append(NO_REPEAT_HEADER);
        for (String type : splitStr) {
            switch (type.trim()) {
                case "ip":
                    String ipAddress = getIPAddress(request);
                    if (!StringUtils.isEmpty(ipAddress)) {
                        ipAddress = ipAddress.replaceAll(":","");
                        appendKey(key, ipAddress);
                        appendKey(key, ":");
                    }else {
                        log.warn("【防重注解】访问{}路径时,ip地址未获取到",request.getServletPath());
                        appendKey(key, "null:");
                    }
                    log.debug("【防重访问ip ipAddress】" + ipAddress);
                    break;
                case "path":
                    String servletPath = request.getServletPath();
                    log.debug("【防重访问路径 servletPath】" + servletPath);
                    appendKey(key, servletPath);
                    break;
                case "params":
                    Object[] args = point.getArgs();
                    String[] parameterNames = signature.getParameterNames();
                    pushParamsToKey(args, key, annotation, parameterNames);

                    break;
                case "token":
                    String accessToken = request.getHeader("token");
                    log.debug("【防重访问token currentUserToken】" + accessToken);
                    appendKey(key, accessToken);
                    break;
                default:
                    throw new RuntimeException("【未在 NoRepeatSumbitCheckType 中配置匹配类型】" + type.trim());
            }
        }
        if (NO_REPEAT_HEADER.equals(key)) {
            throw new RuntimeException("【防重主键为空】");
        }
        return key.substring(0, key.length() - 1);


    }

    /**
     * @param args
     * @param key
     * @param annotation
     * @param parameterNames
     * @方法描述:通过 a.b.c 的结构在请求参数中拿到 c的value值
     */
    private void pushParamsToKey(Object[] args, StringBuilder key, NoRepeatSubmit annotation, String[] parameterNames) {
        String debugStrNeed = "";

        //防重需要的请求参数names
        String[] strings = annotation.checkRequestParams();
        //判断传入参数
        if (args == null || args.length <= 0) {
            throw new RuntimeException("请求参数为空" + Arrays.toString(args));
        }
        //遍历checkRequestParams
        for (String string : strings) {
            Object value = null;
            for (int i = 0; i < args.length; i++) {
                //如果arg类型为基础数据类型,直接push到key
                Object argsOne = args[i];
                if (isBaseType(argsOne)) {
                    if (string.equals(parameterNames[i])) {
                        appendKey(key, argsOne);
                        break;
                    }
                    continue;
                }
                // 如果参数类型为数组,只取第一个对象的键值对信息
                Object transformObj = null;
                if (argsOne instanceof Object[]){
                    Object[] arg = (Object[]) argsOne;
                    if (arg.length <= 0) continue;
                    transformObj = arg[0];

                }else if (argsOne instanceof List){
                    List arg = (List) argsOne;
                    if (arg.size() <=0) continue;
                    transformObj = arg.get(0);
                }else {
                    transformObj = argsOne;
                }
                JSONObject argJson = JSON.parseObject(JSON.toJSONString(transformObj));
                //不存在多级
                if (string.indexOf(".") == -1) {
                    value = argJson.get(string);
                } else {
                    //存在多级
                    String[] split = string.split("\\.");
                    int length = split.length;
                    Object remainTemp = argJson.get(split[0]);
                    JSONObject transJson;
                    try {
                        for (int j = 1; j < length; j++) {
                            if (remainTemp instanceof JSONObject) {
                                transJson = (JSONObject) remainTemp;
                                remainTemp = transJson.get(split[j]);
                            }
                        }
                        value = remainTemp;
                    } catch (Exception e) {
                        log.error("【防重注解 params结构错误】");
                        log.error("【错误信息】" + e.getMessage());
                        return;
                    }

                }
            }
            appendKey(key, value);
            if (log.isDebugEnabled()){
                debugStrNeed += value + "#";
            }
        }
        if (log.isDebugEnabled()){
            log.debug("【防重访问params】{}",debugStrNeed);
        }
    }

    private boolean isBaseType(Object arg) {
        if (arg instanceof String) {
            return true;
        } else if (arg instanceof Integer) {
            return true;
        } else if (arg instanceof Boolean) {
            return true;
        } else if (arg instanceof Float) {
            return true;
        } else if (arg instanceof Long) {
            return true;
        } else if (arg instanceof Double) {
            return true;
        } else if (arg instanceof Short) {
            return true;
        } else if (arg instanceof Byte) {
            return true;
        } else {
            return false;
        }
    }


    private void appendKey(StringBuilder key, Object value) {
        if (!ObjectUtils.isEmpty(value)) {
            key.append(value + "#");

        }
    }


    /**
     * @param request
     * @return
     * @方法描述:获取用户的ip地址
     */
    private String getIPAddress(HttpServletRequest request) {
        String ip = null;

        //X-Forwarded-For:Squid 服务代理
        String ipAddresses = request.getHeader("X-Forwarded-For");

        if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
            //Proxy-Client-IP:apache 服务代理
//            logger.info("【Proxy-Client-IP】");
            ipAddresses = request.getHeader("Proxy-Client-IP");
        }

        if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
            //WL-Proxy-Client-IP:weblogic 服务代理
//            logger.info("【WL-Proxy-Client-IP】");
            ipAddresses = request.getHeader("WL-Proxy-Client-IP");
        }

        if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
            //HTTP_CLIENT_IP:有些代理服务器
//            logger.info("【HTTP_CLIENT_IP】");
            ipAddresses = request.getHeader("HTTP_CLIENT_IP");
        }

        if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
            //X-Real-IP:nginx服务代理
//            logger.info("【X-Real-IP】");
            ipAddresses = request.getHeader("X-Real-IP");
        }

        //有些网络通过多层代理,那么获取到的ip就会有多个,一般都是通过逗号(,)分割开来,并且第一个ip为客户端的真实IP
        if (ipAddresses != null && ipAddresses.length() != 0) {
            ip = ipAddresses.split(",")[0];
        }

        //还是不能获取到,最后再通过request.getRemoteAddr();获取
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
//            logger.info("【request.getRemoteAddr】");
            ip = request.getRemoteAddr();
        }
        return ip;
    }
}



  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310

有用的话,别忘记点赞呦!!!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/234358
推荐阅读
相关标签
  

闽ICP备14008679号