日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

springboot token_Springboot接口幂等性基于token实现方案

發布時間:2025/3/19 javascript 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 springboot token_Springboot接口幂等性基于token实现方案 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

什么是接口冪等

冪等(idempotent、idempotence)是一個數學與計算機學概念,常見于抽象代數中,即f(f(x)) = f(x).簡單的來說就是一個操作多次執行產生的結果與一次執行產生的結果一致。有些系統操作天生就具有冪等性例如數據庫的select語句,但更多時候是需要程序員來做保證的,尤其是在分布式系統環境中,接口能不能做到保證冪等性對系統的影響可能是非常大的,例如很常見的支付下單等場景,由于分布式環境中網絡的復雜性,用戶誤操作,網絡抖動,消息重復,服務超時導致業務自動重試等等各種情況都可能會使線上數據產生了不一致,造成生產事故。

實現方案

1、查詢操作:查詢一次和查詢多次,在數據不變的情況下,查詢結果是一樣的。select是天然的冪等操作;2、刪除操作:刪除操作也是冪等的,刪除一次和多次刪除都是把數據刪除。(注意可能返回結果不一樣,刪除的數據不存在,返回0,刪除的數據多條,返回結果多個) ;3、唯一索引:利用數據庫新增臟數據。比如:支付寶的資金賬戶,支付寶也有用戶賬戶,每個用戶只能有一個資金賬戶,怎么防止給用戶創建資金賬戶多個,那么給資金賬戶表中的用戶ID加唯一索引,所以一個用戶新增成功一個資金賬戶記錄。要點:唯一索引或唯一組合索引來防止新增數據存在臟數據(當表存在唯一索引,并發時新增報錯時,再查詢一次就可以了,數據應該已經存在了,返回結果即可);4、token機制:防止頁面重復提交。采用token加redis或token加jvm內存。處理流程:1. 數據提交前要向服務的申請token,token放到redis或jvm內存,token有效時間;2. 提交后后臺校驗token,同時刪除token,生成新的token返回。token特點:要申請,一次有效性,可以限流。注意:redis要用刪除操作來判斷token,刪除成功代表token校驗通過,如果用select+delete來校驗token,存在并發問題,不建議使用;5、分布式鎖:如果是分布式系統的話,構建全局唯一索引會比較困難,比如唯一性的字段就沒有辦法確定。這時候可以引入分布式鎖,通過第三方的系統(Redis或Zookeeper),在業務系統插入數據或者更新數據前,需要先獲取分布式鎖,然后才能做操作,操作完成之后就釋放鎖。這樣其實是把單機系統里面多線程并發鎖的思路引入了多個系統的場景,也就是分布式系統中的解決思路。要點:某個長流程處理過程要求不能并發執行,可以在流程執行之前根據某個標志(用戶ID+后綴等)獲取分布式鎖,其他流程執行時獲取鎖就會失敗,也就是同一時間該流程只能有一個能執行成功,執行完成后,釋放分布式鎖(分布式鎖要第三方系統提供)。6、select + insert:在設計單據相關的業務,或者是任務相關的業務,肯定會涉及到狀態機(狀態變更圖)。簡單理解,就是業務單據上面有個狀態的字段,狀態在不同的情況下會發生變更,一般情況下存在有限狀態機。這時候,如果狀態機已經處于下一個狀態,這時候來了一個上一個狀態的變更,理論上是不能夠變更的,這樣的話,保證了有限狀態機的冪等。注意:訂單等單據類業務,存在很長的狀態流轉,一定要深刻理解狀態機,對業務系統設計能力提高有很大幫助。

基于token+Redis的實現方案

環境:springboot2.2.11.RELEASE + Redis

  • pom.xml 依賴
org.springframework.bootspring-boot-starter-data-redisorg.springframework.bootspring-boot-starter-weborg.apache.commonscommons-pool2
  • 自定義注解類,有該注解的需要驗證token是否有效
@Documented@Inherited@Retention(RUNTIME)@Target({ METHOD, TYPE})public @interface ApiIdempotent {}
  • 攔截器定義,攔截請求方法進行token有效性驗證
public class MethodIdempotentCheck implements HandlerInterceptor {private Logger logger = LoggerFactory.getLogger(MethodIdempotentCheck.class) ;@Resourceprivate StringRedisTemplate stringRedisTemplate ;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {if (handler instanceof HandlerMethod) {HandlerMethod handlerMethod = (HandlerMethod) handler ;Method method = handlerMethod.getMethod() ;Class> clazz = method.getClass() ;if (clazz.isAnnotationPresent(ApiIdempotent.class)) {if (!checkToken(request)) {failure(response) ;return false ;}} else {if (method.isAnnotationPresent(ApiIdempotent.class)) {if (!checkToken(request)) {failure(response) ;return false ;}}}}return true ;}private void failure(HttpServletResponse response) throws Exception {response.setContentType("application/json;charset=utf-8") ;response.getWriter().write("{"code": -1, "message": "重復提交"}") ;}private boolean checkToken(HttpServletRequest request) {logger.info("驗證token") ;String token = request.getParameter("access-token") ;if (token == null || token.length() == 0) {token = request.getHeader("access-token") ;}logger.info("獲取token:{}", token) ;if (token == null || token.length() == 0) {return false ;}boolean exists = stringRedisTemplate.hasKey(token) ;if (!exists) {return false ;}return stringRedisTemplate.delete(token) ;}}token會從header中獲取請求參數中獲取。
  • WebConfig 配置攔截器
@Configurationpublic class WebConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(tokenInterceptor()) ;}@Beanpublic HandlerInterceptor tokenInterceptor() {return new MethodIdempotentCheck() ;}}
  • Controller 測試
@RestController@RequestMapping("/demo")public class DemoController {@Resourceprivate StringRedisTemplate stringRedisTemplate ;/** * 生成Token;這里token的有效期設置了10分鐘。 * @return */@GetMapping("/create")public Object create() {String access_token = UUID.randomUUID().toString() ;stringRedisTemplate.opsForValue().set(access_token, access_token, 10, TimeUnit.MINUTES) ;return access_token ;}@PostMapping("/save")@ApiIdempotentpublic Object business(@RequestBody Users user) {Map result = new HashMap<>() ;// todo save Usersresult.put("code", 0) ;result.put("message", "創建成功") ;return result ;}}

business 方法加入了@ApiIdempotent注解,表示該方法需要進行token驗證是否有效的請求

整個請求流程要先獲取token,然后將得到的token放入header中或者請求參數中。

測試:

獲取token:

請求業務方法將獲取的token 添加到header中

再次請求:

每次請求token的驗證是通過刪除token進行的,所以當第二次再請求時,redis中已經沒有了token所以這里就提示:重復提交了。

完畢!!!

給個關注,轉發,謝謝

SpringBoot RabbitMQ消息可靠發送與接收

SpringBoot中使用Cache及JSR107的使用

SpringBoot開發自己的Starter

SpringBoot開發自己的@Enable功能

Java線上CPU100% 問題排查

Restful API設計規范

SpringCloud Nacos 服務動態配置

SpringCloud Nacos 服務消費者

SpringCloud Nacos 服務提供者

SpringCloud Alibaba 之 Nacos 服務

Spring Cloud Nacos 開啟權限驗證

SpringCloud Nacos 整合feign

SpringCloud Hystrix實現資源隔離應用

Alibaba Sentinel動態規則(Nacos數據源)

SpringCloud zuul 動態網關配置

總結

以上是生活随笔為你收集整理的springboot token_Springboot接口幂等性基于token实现方案的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。