当前位置: 首页 > news >正文

使用SpringBoot自定义注解+拦截器+token机制,实现接口的幂等性

1 整合springboot和redis环境的集成

2 配置请求的方法体和枚举类

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;@Data
@AllArgsConstructor
@NoArgsConstructor
public class Response {private int status;private String msg;private Object data;// 省略get、set、toString、无参有参构造方法
}///import lombok.Getter;@Getter
public enum ResponseCode {// 通用模块 1xxxxILLEGAL_ARGUMENT(10000, "参数不合法"),REPETITIVE_OPERATION(10001, "请勿重复操作"),;ResponseCode(Integer code, String msg) {this.code = code;this.msg = msg;}private Integer code;private String msg;
}

3 自定义异常以及配置全局异常类

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;@Data
@AllArgsConstructor
@NoArgsConstructor
public class ServiceException extends RuntimeException {private String code;private String msg;// 省略get、set、toString以及构造方法
}import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;@ControllerAdvice
public class MyControllerAdvice {@ResponseBody@ExceptionHandler(ServiceException.class)public Response serviceExceptionHandler(ServiceException exception) {Response response = new Response(Integer.valueOf(exception.getCode()), exception.getMsg(), null);return response;}
}

4 编写创建Token和验证Token的接口以及实现类【核心类】

import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;@Service
public interface TokenService {public Response createToken();public Response checkToken(HttpServletRequest request);
}//import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import java.util.UUID;@Service
public class TokenServiceImpl implements TokenService {@Autowiredprivate RedisTemplate redisTemplate;@Overridepublic Response createToken() {// 生成uuid当作tokenString token = UUID.randomUUID().toString().replaceAll("-", "");// 将生成的token存入redis中redisTemplate.opsForValue().set(token, token);// 返回正确的结果信息Response response = new Response(0, token.toString(), null);return response;}@Overridepublic Response checkToken(HttpServletRequest request) {// 从请求头中获取tokenString token = request.getHeader("token");if (StringUtils.isBlank(token)) {// 如果请求头token为空就从参数中获取token = request.getParameter("token");// 如果都为空抛出参数异常的错误if (StringUtils.isBlank(token)) {throw new ServiceException(ResponseCode.ILLEGAL_ARGUMENT.getCode().toString(), ResponseCode.ILLEGAL_ARGUMENT.getMsg());}}// 如果redis中不包含该token,说明token已经被删除了,抛出请求重复异常if (!redisTemplate.hasKey(token)) {throw new ServiceException(ResponseCode.REPETITIVE_OPERATION.getCode().toString(), ResponseCode.REPETITIVE_OPERATION.getMsg());}// 删除tokenBoolean del = redisTemplate.delete(token);// 如果删除不成功(已经被其他请求删除),抛出请求重复异常if (!del) {throw new ServiceException(ResponseCode.REPETITIVE_OPERATION.getCode().toString(), ResponseCode.REPETITIVE_OPERATION.getMsg());}return new Response(0, "校验成功", null);}
}

5 配置需要幂等的自定义注解

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 ApiIdempotent {
}

6 接口拦截器

public class ApiIdempotentInterceptor implements HandlerInterceptor {@Autowiredprivate TokenService tokenService;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {if (!(handler instanceof HandlerMethod)) {return true;}HandlerMethod handlerMethod = (HandlerMethod) handler;Method method = handlerMethod.getMethod();ApiIdempotent methodAnnotation = method.getAnnotation(ApiIdempotent.class);if (methodAnnotation != null) {// 校验通过放行,校验不通过全局异常捕获后输出返回结果tokenService.checkToken(request);}return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {}
}

7 配置拦截器

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
public class WebConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(apiIdempotentInterceptor());}@Beanpublic ApiIdempotentInterceptor apiIdempotentInterceptor() {return new ApiIdempotentInterceptor();}
}

8 写个controller测试

import com.example.demo_26.mindeng_3.domain.Food;
import com.example.demo_26.mindeng_3.mapper.FoodMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;@RestController
@RequestMapping("/token")
public class TokenController {@Autowiredprivate TokenService tokenService;@Autowiredprivate FoodMapper tBookOrderMapper;@GetMappingpublic Response token() {return tokenService.createToken();}@GetMapping("/checktoken")@ApiIdempotent()public String demo(@RequestBody Food tBookOrder) {// 处理请求int insert = tBookOrderMapper.insert(tBookOrder);return insert==1?"ok":"wrong";}
}

9 测试

首先,通过调用/token接口生成一个token。
curl -X GET http://localhost:8080/token然后,使用生成的token调用/token/checktoken接口进行验证。
curl -X POST http://localhost:8080/token/checktoken -H "token: generated-token"如果再次使用相同的token调用,将会得到重复操作的错误:这样,我们就通过token机制实现了接口的幂等性


http://www.mrgr.cn/news/43545.html

相关文章:

  • 【go入门】流程控制语句
  • 51c视觉~CV~合集3
  • 基于Java的GeoTools对Shapefile文件属性信息深度解析
  • C语言进阶版第16课—自定义类型:结构体
  • webserver
  • 网络基础知识笔记(五)接口管理
  • 已解决-Nacos明明成功运行,但Spring报错连接不上
  • 《Linux从小白到高手》理论篇:一文概览常用Linux重要配置文件
  • 免费论文生成网站有哪些?推荐5款AI自动生成论文的网站
  • 基于SPI协议的Flash扇区擦除实验
  • 算法: 二分查找题目练习
  • Flex布局
  • Oracle数据库中表压缩的实现方式和特点
  • Java中的break、continue和return语句
  • 12条职场经验总结
  • czx前端
  • Tkinter打包成EXE安装文件
  • AI资深导师指导-ChatGPT深度科研工作应用、论文撰写、数据分析及机器学习与AI绘图
  • Markdown 语法详解大全(超级版)(三)——甘特图语法详解
  • 存储电话号码的数据类型,用 int 还是用 string?