欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringBoot實現(xiàn)接口等冪次校驗的示例代碼

 更新時間:2022年01月12日 10:04:13   作者:one_smail  
本文主要介紹了SpringBoot實現(xiàn)接口等冪次校驗的示例代碼,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下

接口等冪性通俗的來說就是同一時間內(nèi),發(fā)起多次請求只有一次請求成功;其目的時防止多次提交,數(shù)據(jù)重復(fù)入庫,表單驗證網(wǎng)絡(luò)延遲重復(fù)提交等問題。

比如:

  • 訂單接口, 不能多次創(chuàng)建訂單
  • 支付接口, 重復(fù)支付同一筆訂單只能扣一次錢
  • 支付寶回調(diào)接口, 可能會多次回調(diào), 必須處理重復(fù)回調(diào)
  • 普通表單提交接口, 因為網(wǎng)絡(luò)超時等原因多次點擊提交, 只能成功一次
    等等

主流的實現(xiàn)方案如下:

1、唯一索引:給表加唯一索引,該方法最簡單,當(dāng)數(shù)據(jù)重復(fù)插入時,直接報sql異常,對應(yīng)用影響不大;

alter table 表名 add unique(字段)

示例,兩個字段為唯一索引,如果出現(xiàn)完全一樣的order_name,create_time就直接重復(fù)報異常;

alter table 'order' add unique(order_name,create_time)

2、先查詢后判斷:入庫時先查詢是否有該數(shù)據(jù),如果沒有則插入。否則不插入;

3、token機制:發(fā)起請求的時候先去redis獲取token,將獲取的token放入請求的hearder,當(dāng)請求到達服務(wù)端的時候攔截請求,對請求的hearder中的token,進行校驗,如果校驗通過則放開攔截,刪除token,否則使用自定義異常返回錯誤信息。

第一步:書寫redis工具類

?
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
 
@Component
public class RedisUtils {
 
    @Autowired
    private RedisTemplate<String,Object> redisTemplate;
 
    /**
     * 判斷key是否存在
     * @param key 鍵
     * @return
     */
    public boolean hasKey(String key){
        try {
            return redisTemplate.hasKey(key);
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
    }
 
    /**
     * 刪除key
     * @param key 鍵
     * @return
     */
    public Boolean del(String key){
        if (key != null && key.length() > 0){
            return redisTemplate.delete(key);
        }else {
            return false;
        }
    }
 
    /**
     * 普通緩存獲取
     * @param key 鍵
     * @return
     */
    public Object get(String key){
        return key == null ? null : redisTemplate.opsForValue().get(key);
    }
 
    /**
     * 普通緩存放入并設(shè)置時間
     * @param key 鍵
     * @param value 值
     * @param time 時間(秒) time要大于0 如果time小于等于0 將設(shè)置無限期
     * @return
     */
    public boolean set(String key,Object value,long time){
        try {
            if (time > 0){
                redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
            }
            return true;
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
    }
}

?

第二步、書寫token工具類

import com.smile.project.exception.utils.CodeMsg;
import com.smile.project.exception.utils.CommonException;
import io.netty.util.concurrent.GlobalEventExecutor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;
import javax.servlet.http.HttpServletRequest;
import java.util.UUID;
 
/**
 * 使用uuid生成隨機字符串,
 * 通過md5加密防止token被解密,保證token的唯一性與安全性
 * 設(shè)置過期時間為30秒,即在30秒內(nèi)之惡能提交成功一次請求
 */
@Component
public class TokenUtils {
 
    @Autowired
    RedisUtils redisUtils;
 
    //token過期時間為30秒
    private final static Long TOKEN_EXPIRE = 30L;
 
    private final static String TOKEN_NAME = "token";
 
    /**
     * 生成token放入緩存
     */
    public String generateToken(){
        String uuid = UUID.randomUUID().toString();
        String token = DigestUtils.md5DigestAsHex(uuid.getBytes());
        redisUtils.set(TOKEN_NAME,token,TOKEN_EXPIRE);
        return token;
    }
 
    /**
     * token校驗
     */
    public boolean verifyToken(HttpServletRequest request){
        String token = request.getHeader(TOKEN_NAME);
        //header中不存在token
        if (StringUtils.isEmpty(token)){
            //拋出自定義異常
            System.out.println("token不存在");
            throw new CommonException(CodeMsg.NOT_TOKEN);
        }
        //緩存中不存在
        if (!redisUtils.hasKey(TOKEN_NAME)){
            System.out.println("token已經(jīng)過期");
            throw new CommonException(CodeMsg.TIME_OUT_TOKEN);
        }
        String cachToken = (String) redisUtils.get(TOKEN_NAME);
        if (!token.equals(cachToken)){
            System.out.println("token檢驗失敗");
            throw new CommonException(CodeMsg.VALIDA_ERROR_TOKEN);
        }
        //移除token
        Boolean del = redisUtils.del(TOKEN_NAME);
        if (!del){
            System.out.println("token刪除失敗");
            throw new CommonException(CodeMsg.DEL_ERROR_TOKEN);
        }
        return true;
    }
}

第三步:定義注解,使用在方法上,當(dāng)控制層的方法上被注釋時,表示該請求為等冪性請求

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
 /**
 * 當(dāng)控制層的方法上被注釋時,表示該請求為等冪性請求
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Idempotent {
}

第四步:攔截器配置。選擇前置攔截器,每次請求都校驗到達的方法上是否有等冪性注解,如果有則進行token校驗

import com.smile.project.redis.utils.Idempotent;
import com.smile.project.redis.utils.TokenUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
?
@Component
public class IdempotentInterceptor implements HandlerInterceptor {
?
? ? @Autowired
? ? private TokenUtils tokenUtils;
?
? ? @Override
? ? public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
? ? ? ? if (!(handler instanceof HandlerMethod)){
? ? ? ? ? ? return true;
? ? ? ? }
? ? ? ? //對有Idempotent注解的方法進行攔截校驗
? ? ? ? HandlerMethod handlerMethod = (HandlerMethod) handler;
? ? ? ? Method method = handlerMethod.getMethod();
? ? ? ? Idempotent methodAnnotation = method.getAnnotation(Idempotent.class);
? ? ? ? if (methodAnnotation != null){
? ? ? ? ? ? //token校驗
? ? ? ? ? ? tokenUtils.verifyToken(request);
? ? ? ? }
? ? ? ? return true;
? ? }
?
? ? @Override
? ? public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
? ? }
?
? ? @Override
? ? public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
? ? }
}

第五步:對攔截器進行url模式匹配,并注入spring容器

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
 
/**
 * 對攔截器進行url模式匹配,并注入spring容器
 */
@Configuration
public class WebConfiguration implements WebMvcConfigurer {
 
    @Autowired
    IdempotentInterceptor idempotentInterceptor;
 
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //攔截所有請求
        registry.addInterceptor(idempotentInterceptor).addPathPatterns("/**");
    }
}

第六步:控制層

對控制層進行編寫,發(fā)起請求時通過getToken方法獲取token,將獲取的token放入hearder后,再請求具體方法。正常請求具體方法的時候注意在hearder中加入token,否則是失敗

import com.alibaba.fastjson.JSONObject;
import com.smile.project.exception.utils.CodeMsg;
import com.smile.project.exception.utils.ResultPage;
import com.smile.project.redis.utils.Idempotent;
import com.smile.project.redis.utils.TokenUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
 
@RestController
public class SmileController {
 
    @Autowired
    TokenUtils tokenUtils;
 
    @GetMapping("smile/token")
    public ResultPage getToken(){
        String token = tokenUtils.generateToken();
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("token",token);
        return ResultPage.success(CodeMsg.SUCCESS,jsonObject);
    }
 
    @Idempotent
    @GetMapping("smile/test")
    public ResultPage testIdempotent(){
        return ResultPage.success(CodeMsg.SUCCESS,"校驗成功");
    }
}

到此這篇關(guān)于SpringBoot實現(xiàn)接口等冪次校驗的示例代碼的文章就介紹到這了,更多相關(guān)SpringBoot實現(xiàn)接口等冪次校驗 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot實現(xiàn)自定義啟動器的示例詳解

    SpringBoot實現(xiàn)自定義啟動器的示例詳解

    雖然Spring官方給我們提供了很多的啟動器供我們使用,但有時候我們也會遇到某些特殊場景,這些啟動器滿足不了。這個時候就需要自定義一個啟動器供我們使用,本文為大家介紹了SpringBoot實現(xiàn)自定義啟動器的方法,希望對大家有所幫助
    2023-01-01
  • Java中Synchronized的用法解析

    Java中Synchronized的用法解析

    synchronized是Java中的關(guān)鍵字,是一種同步鎖,本文給大家詳細介紹Java Synchronized 用法大全,感興趣的朋友跟隨小編一起看看吧
    2021-11-11
  • Java中List排序的三種實現(xiàn)方法實例

    Java中List排序的三種實現(xiàn)方法實例

    其實Java針對數(shù)組和List的排序都有實現(xiàn),對數(shù)組而言你可以直接使用Arrays.sort,對于List和Vector而言,你可以使用Collections.sort方法,下面這篇文章主要給大家介紹了關(guān)于Java中List排序的三種實現(xiàn)方法,需要的朋友可以參考下
    2021-12-12
  • 基于Spring中的事務(wù)@Transactional細節(jié)與易錯點、幻讀

    基于Spring中的事務(wù)@Transactional細節(jié)與易錯點、幻讀

    這篇文章主要介紹了基于Spring中的事務(wù)@Transactional細節(jié)與易錯點、幻讀,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • Java中Stream的flatMap與map使用場景及區(qū)別詳解

    Java中Stream的flatMap與map使用場景及區(qū)別詳解

    這篇文章主要介紹了Java中Stream的flatMap與map使用場景及區(qū)別詳解,Stream 流式操作,一般用于操作集合即 List 一類的數(shù)據(jù)結(jié)構(gòu),簡單來說 Stream 的 map 使得其中的元素轉(zhuǎn)為另一種元素的映射(map)方法,需要的朋友可以參考下
    2024-01-01
  • java的三種隨機數(shù)生成方式

    java的三種隨機數(shù)生成方式

    主要介紹了java的三種隨機數(shù)生成方式的實現(xiàn)方法,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,,需要的朋友可以參考下
    2021-07-07
  • SpringBoot讀取Resource目錄下文件的四種方式總結(jié)

    SpringBoot讀取Resource目錄下文件的四種方式總結(jié)

    在Spring?Boot項目中,經(jīng)常需要獲取resources目錄下的文件,這些文件可以包括配置文件、模板文件、靜態(tài)資源等,本文將介紹四種常用的方法來獲取resources目錄下的文件,需要的朋友可以參考下
    2023-08-08
  • 最新評論