当前位置:   article > 正文

token 方式处理接口幂等性

用token实现接口幂等性

方式系统接口的重复调用,在分布式系统中A服务重复向B服务发送指令,导致B服务重复消费消息。
在单体系统中类似新增操作重复指令,导致系统参数多天重复数据。

方式一
数据库增加唯一索引,保证数据的唯一性方式二
接口增加幂等性校验
具体方式如下:
①定义注解

  1. @Target(ElementType.METHOD)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. public @interface Ide {
  5. }

②aop 实现

  1. @Aspect
  2. @Component
  3. public class IdeAspect {
  4. protected Logger logger = LoggerFactory.getLogger(getClass());
  5. @Autowired
  6. private RedisTemplate redisTemplate;
  7. private final String msg ="token 失效,请刷新页面后再进行提交!";
  8. @Pointcut("@annotation(com.laiease.common.annotation.Ide)")
  9. public void idePointCut() {
  10. }
  11. @Around("idePointCut()")
  12. public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
  13. String methodName = joinPoint.getSignature().getName();
  14. HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
  15. JSONObject json = JSONObject.parseObject(HttpHelper.getBodyString(request));
  16. Optional<JSONObject> optional = Optional.ofNullable(json);
  17. String ide = optional.map(item -> item.getString("ide")).orElseThrow(() -> new RuntimeException(msg));
  18. Map map = (Map) redisTemplate.opsForValue().get(ide);
  19. if (map!=null&&(methodName.equals(map.get("method"))&&1==(int)map.get("status"))) {
  20. map.put("status",0);
  21. redisTemplate.opsForValue().set(ide, map);
  22. } else {
  23. throw new RuntimeException(msg);
  24. }
  25. R r = (R) joinPoint.proceed();
  26. Map result = (Map) r.get("data");
  27. if(null!= result.get("status") && (boolean)result.get("status")){
  28. redisTemplate.delete(ide);
  29. }else{
  30. map.put("status",1);
  31. redisTemplate.opsForValue().set(ide, map);
  32. }
  33. return r;
  34. }
  35. @AfterThrowing(pointcut = "idePointCut()", throwing = "ex")
  36. public void afterThrowing(Throwable ex) {
  37. if (!msg.equals(ex.getMessage())) {
  38. HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
  39. JSONObject json = JSONObject.parseObject(HttpHelper.getBodyString(request));
  40. String ide = json.getString("ide");
  41. Map map = (Map) redisTemplate.opsForValue().get(ide);
  42. if (map != null) {
  43. map.put("status", 1);
  44. redisTemplate.opsForValue().set(ide, map);
  45. }
  46. }
  47. }
  48. }

③定义获取token的接口供前端调用,实现接口可以使用雪花算法或者其他

  1. @Override
  2. public String getMethodsToken(JSONObject jsonObject) {
  3. Map map = new HashMap();
  4. Optional<JSONObject> optional = Optional.ofNullable(jsonObject);
  5. String method = optional.map(item -> item.getString("method")).orElseThrow(() -> new RuntimeException("异常操作"));
  6. String ide = CmUtil.getUUID();
  7. map.put("method",method);
  8. map.put("status",1);
  9. redisTemplate.opsForValue().set(ide, map);
  10. return ide;
  11. }

④使用
在Controller 层 新增接口或者其他方法接口上增加注解

  1. @Ide
  2. @Lelog("保存用户")
  3. @ApiOperation(value = "保存用户")
  4. @PostMapping(value = "/save")
  5. public R save(@RequestBody User user) throws Exception {
  6. return R.ok(userService.saveOne(user));
  7. }

补充:HttpHelper 工具类

  1. @Slf4j
  2. public class HttpHelper {
  3. private final static String xssWhitelist = PropertiesUtil.builder("config.properties").getProperty("sys.xss.whitelist");
  4. public static String getBodyString(HttpServletRequest request) {
  5. StringBuilder sb = new StringBuilder();
  6. String result = "";
  7. try (InputStream inputStream = request.getInputStream();
  8. BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")))) {
  9. String line;
  10. while ((line = reader.readLine()) != null) {
  11. sb.append(line);
  12. }
  13. result = sb.toString();
  14. if (!isUrlPass(request.getRequestURI())) {
  15. // xss sql 过滤
  16. result = xssSqlLeach(result);
  17. }
  18. } catch (IOException e) {
  19. log.error(e.toString());
  20. }
  21. return result;
  22. }
  23. private static String xssSqlLeach(String body) {
  24. if (body == null || body.isEmpty()) {
  25. return body;
  26. }
  27. StringBuilder sb = new StringBuilder(body.length());
  28. for (int i = 0; i < body.length(); i++) {
  29. char c = body.charAt(i);
  30. switch (c) {
  31. case '>':
  32. sb.append("》");// 转义大于号
  33. break;
  34. case '<':
  35. sb.append("《");// 转义小于号
  36. break;
  37. case '\'':
  38. sb.append("‘");// 转义单引号
  39. break;
  40. case '\"':
  41. sb.append('"');// 转义双引号
  42. break;
  43. case '&':
  44. sb.append("&");// 转义&
  45. break;
  46. default:
  47. String s1 = c + "";
  48. String s = s1.replaceAll(".*([';]+|(--)+).*", "");
  49. sb.append(s);
  50. break;
  51. }
  52. }
  53. return sb.toString();
  54. }
  55. private static boolean isUrlPass(String url) {
  56. String[] urlList = xssWhitelist.split(";");
  57. return ArrayUtils.contains(urlList, url);
  58. }
  59. }

使用redis 分布式锁处理接口幂等性 redis 分布式锁处理接口幂等性

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号