思路:首先是个springcloud项目,需要配好nacos对redis的连接,然后redis也要在自己的配置文件配好支持本地ip的访问设置,不是原来的127.0.0.1。然后需要自定义接口,以及对自定义注解进行调用的类进行控制,使自定义注解在controller某个方法中可以实现重复访问的控制。

nacos中配置的redis连接

spring:
  redis:
    host: 10.11.6.82
    port: 6379
    password: 123456
    lettuce:
      pool:
        enabled: true
        max-active: 100
        max-wait: -1
        max-idle: 50
        min-idle: 5

redis本身的配置

//1.redis.windows.conf配置文件
//需要notepad++编辑 全局搜索protected-mode默认是yes,需要改成no
//搜索bind 把这行配置的127.0.0.1给注释掉#
//以下是改好的效果
protected-mode no
#bind 127.0.0.1
//2.redis.windows.service,conf配置同上,也修改那两个地方 省略步骤。。。
//3.之后用redis可视化软件连接redis
//案例中是ip是 10.11.6.82 改成你自己的
//       post是6379
//       password是123456 改成你自己的

项目中自定义注解

import java.lang.annotation.*;

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

    String channel() default "APP";

    int expireTime() default 3;
}

项目中操作自定义注解的类

import cn.hutool.core.util.StrUtil;
import com.dykj.common.base.utils.RegUtil;
import com.dykj.common.core.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
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.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

@Component
@Aspect
@Slf4j
public class CheckRepeatCommitAspect {
    @Autowired
    private RegUtil regUtil;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Pointcut("@annotation(com.dykj.file.utils.CheckRepeatCommit)")
    private void checkRepeatCommit() {

    }

    @Around("checkRepeatCommit()")
    public Object checkRepeatCommit(ProceedingJoinPoint joinPoint) throws Throwable {

        String methodName = joinPoint.getSignature().getName();
        Object target = joinPoint.getTarget();
        //得到拦截的方法
        Method method = getMethodByClassAndName(target.getClass(), methodName);
        log.info("验证是否重复提交:" + "调用方法:" + method);

        //请求的方法名
        String className = joinPoint.getTarget().getClass().getName();

        String channel = "";
        String bizKey = method.getName();
        int expireTime = 0;

        // 获取当前请求方法的注解,根据注解配置获取参数
        CheckRepeatCommit checkRepeatCommit = method.getAnnotation(CheckRepeatCommit.class);

        //String userNo = SysUserContextHolder.getSysUser().getUserNo();
        String userNo =regUtil.getLoginUserId();
        String key;

        if (checkRepeatCommit != null) {
            //注解上的描述
            channel = checkRepeatCommit.channel();
            expireTime = checkRepeatCommit.expireTime();

            key = getRepeatCommitLock(channel, className, bizKey, userNo, expireTime);

            if (StringUtils.isBlank(key)) {
                throw new BusinessException("ResultEnum.RE_COMMIT");
            }
        }
        return joinPoint.proceed();
    }

    /**
     * 根据类和方法名得到方法
     */
    public Method getMethodByClassAndName(Class c, String methodName) {
        Method[] methods = c.getDeclaredMethods();
        for (Method method : methods) {
            if (method.getName().equals(methodName)) {
                return method;
            }
        }
        return null;
    }

    public String getRepeatCommitLock(String channel, String module, String bizKey, String userNo, int expireTime) {

        if (StringUtils.isEmpty(module) || StringUtils.isEmpty(bizKey)) {
            throw new BusinessException("getRepeatCommitLock{} 参数不能为空!");
        }

        String redisKey = channel + StrUtil.COLON + module + StrUtil.COLON + bizKey + StrUtil.COLON + userNo;

        long count = redisTemplate.opsForValue().increment(redisKey, 1);

        if (count == 1) {
            if (expireTime == 0) {
                expireTime = 60;
            }
            redisTemplate.expire(redisKey, expireTime, TimeUnit.SECONDS);
            return redisKey;
        } else {
            return null;
        }
    }

}

controller中需要使用到接口防重复校验的接口

@CheckRepeatCommit
    @PostMapping("One")
    public String One(){
        return "hello! app!";
    }

测试效果

//访问这个接口,快速去redis可视化工具查看到这条存储的信息(时间设置很短,看手速能看到效果)
//访问次数在这个redis这个key存在的期间不能无限访问,需要过了这个时间才可以。

regUtil

import cn.hutool.extra.spring.SpringUtil;
import com.dykj.common.core.redis.utils.RedisUtil;
import com.dykj.mybatis.sys.mapper.SysConfigMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;


@Component
@RequiredArgsConstructor
public class RegUtil {
    private final HttpServletRequest httpServletRequest;
    private final RedisUtil redisUtil;
    /**
     * 获取登录用户信息
     * @return Object 需要类型转换
     */
    public Object getLoginUser(){
        String token = httpServletRequest.getHeader("Authorization");
        return redisUtil.get(token);
    }
    /**
     * 获取登录用户名称
     * @return String
     */
    public String getLoginUserName(){
        String token = httpServletRequest.getHeader("Authorization");
        return JWTUtil.getUserName(token);
    }
    /**
     * 获取登录用户ID
     * @return String
     */
    public String getLoginUserId(){
        String token = httpServletRequest.getHeader("Authorization");
        return JWTUtil.getUserId(token);
    }
    /**
     * 获取登录用户区县代码
     * @return String
     */
    public String getAdministrativeCode(){
        String token = httpServletRequest.getHeader("Authorization");
        return JWTUtil.getadministrativeCode(token);
    }
    /**
     * 获取访问的url
     * @return String
     */
    public String getUrl(){
       return httpServletRequest.getRequestURI();
    }

    /**
     * 获取IP地址
     */
    public String getIpAddr() {
        String ip = null, unknown = "unknown", seperator = ",";
        int maxLength = 15;
        try {
            ip = httpServletRequest.getHeader("x-forwarded-for");
            if (!StringUtils.hasLength(ip) || unknown.equalsIgnoreCase(ip)) {
                ip = httpServletRequest.getHeader("Proxy-Client-IP");
            }
            if (!StringUtils.hasLength(ip) || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
                ip = httpServletRequest.getHeader("WL-Proxy-Client-IP");
            }
            if (!StringUtils.hasLength(ip) || unknown.equalsIgnoreCase(ip)) {
                ip = httpServletRequest.getHeader("HTTP_CLIENT_IP");
            }
            if (!StringUtils.hasLength(ip) || unknown.equalsIgnoreCase(ip)) {
                ip = httpServletRequest.getHeader("HTTP_X_FORWARDED_FOR");
            }
            if (!StringUtils.hasLength(ip) || unknown.equalsIgnoreCase(ip)) {
                ip = httpServletRequest.getRemoteAddr();
            }
        } catch (Exception e) {
            throw e;
        }

        // 使用代理,则获取第一个IP地址
        if (!StringUtils.hasLength(ip) && ip.length() > maxLength) {
            int idx = ip.indexOf(seperator);
            if (idx > 0) {
                ip = ip.substring(0, idx);
            }
        }

        return ip;
    }

    /**
     * 根据key返回系统配置的value
     * @param key 值
     * @return value 值
     */
    public String getConfig(String key){
        SysConfigMapper sysConfigMapper = SpringUtil.getBean(SysConfigMapper.class);
        if(redisUtil.hasKey(3,key))
        {
            return redisUtil.get(3,key).toString();
        }
        String value = sysConfigMapper.getValue(key);
        redisUtil.set(3,key,value);
        return value;
    }
}

抛出异常的工具类

public class BusinessException extends RuntimeException{
    private static final long serialVersionUID = 1L;

    /**
     * 错误码
     */
    private Integer code;

    /**
     * 错误提示
     */
    private String message;

    public BusinessException(String message) {
        this.message = message;
    }

    public BusinessException(int code, String message) {
        this.code = code;
        this.message = message;
    }

    public BusinessException(int code, String msg, Throwable cause) {
        this.message = msg;
        this.code = code;
    }

    public BusinessException(Throwable cause) {
        super(cause);
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

JWT获取token中用户信息

import cn.hutool.core.util.StrUtil;
import io.jsonwebtoken.*;

import java.util.Date;

public class JWTUtil {
    //过期时间
    private static final long tokenExpiration = 24*60*60*1000;
    //签名秘钥
    private static final String tokenSignKey = "xidkngeoiaIdf;ge%dlslgsld;saldfjlkcjvlskjdf39";

    //根据参数生成token
    public static String createToken(String userId, String userName, String administrativeCode) {
        return Jwts.builder()
                .setSubject("DYKJ-USER")
                .setExpiration(new Date(System.currentTimeMillis() + tokenExpiration))
                .claim("userId", userId)
                .claim("userName", userName)
                .claim("administrativeCode",administrativeCode)
                .signWith(SignatureAlgorithm.HS512, tokenSignKey)
                .compressWith(CompressionCodecs.GZIP)
                .compact();
    }

    /*
     * 根据token字符串得到用户id
     * @param token
     */
    public static String getUserId(String token) {
        if(StrUtil.isBlank(token)){
            return null;
        }
        Jws<Claims> claimsJws = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token);
        Claims claims = claimsJws.getBody();
        return claims.get("userId").toString();
    }

    /*
     * 根据token字符串得到用户名称
     * @param token
     */
    public static String getUserName(String token) {
        if(StrUtil.isBlank(token)){
            return null;
        }
        Jws<Claims> claimsJws = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token);
        Claims claims = claimsJws.getBody();
        return (String)claims.get("userName");
    }

    /*
     * 根据token字符串得到区县代码
     * @param token
     */
    public static String getadministrativeCode(String token) {
        if(StrUtil.isBlank(token)){
            return null;
        }
        Jws<Claims> claimsJws = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token);
        Claims claims = claimsJws.getBody();
        return (String)claims.get("administrativeCode");
    }

}