亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

SpringBoot3實(shí)戰(zhàn)教程之實(shí)現(xiàn)接口簽名驗(yàn)證功能

 更新時(shí)間:2025年04月09日 09:03:19   作者:江南一點(diǎn)雨  
接口簽名是一種重要的安全機(jī)制,用于確保 API 請(qǐng)求的真實(shí)性、數(shù)據(jù)的完整性以及防止重放攻擊,這篇文章主要介紹了SpringBoot3實(shí)戰(zhàn)教程之實(shí)現(xiàn)接口簽名驗(yàn)證功能,需要的朋友可以參考下

有時(shí)候我們要把自己的服務(wù)暴露給第三方去調(diào)用,為了防止接口不被授權(quán)訪問(wèn),我們一般采用接口簽名的方式去保護(hù)接口。

接下來(lái)松哥和大家聊一聊這個(gè)話題。

一 場(chǎng)景分析

什么時(shí)候需要接口簽名?

接口簽名是一種重要的安全機(jī)制,用于確保 API 請(qǐng)求的真實(shí)性、數(shù)據(jù)的完整性以及防止重放攻擊。

當(dāng)我們需要保護(hù) API 接口不被未授權(quán)訪問(wèn)、確保傳輸數(shù)據(jù)在過(guò)程中不被篡改,或者需要防止惡意用戶利用 API 進(jìn)行攻擊時(shí),就需要使用接口簽名。

松哥來(lái)舉幾個(gè)需要做接口簽名的例子:

  • 開(kāi)放 API 給第三方使用:當(dāng)你的 API 需要對(duì)外開(kāi)放,讓第三方應(yīng)用或服務(wù)調(diào)用時(shí),接口簽名可以驗(yàn)證請(qǐng)求方的身份,確保只有擁有有效簽名的請(qǐng)求才能被接受。
  • 數(shù)據(jù)完整性校驗(yàn):在數(shù)據(jù)傳輸過(guò)程中,接口簽名可以確保數(shù)據(jù)不被篡改。通過(guò)將請(qǐng)求數(shù)據(jù)與密鑰一起進(jìn)行哈希運(yùn)算,生成簽名值,接收方收到數(shù)據(jù)后可以用相同的方法生成簽名值進(jìn)行對(duì)比,如果一致則數(shù)據(jù)未被篡改。
  • 防止重放攻擊:通過(guò)在簽名中加入時(shí)間戳或隨機(jī)數(shù)等動(dòng)態(tài)元素,接口簽名可以防止攻擊者截獲并重復(fù)發(fā)送有效的 API 請(qǐng)求。
  • 接口防刷:為了防止接口被惡意調(diào)用,通常會(huì)采用一些防刷策略,比如限制請(qǐng)求頻率、使用驗(yàn)證碼等。接口簽名可以作為防刷策略的一部分,確保請(qǐng)求的合法性。
  • 敏感操作驗(yàn)證:對(duì)于涉及敏感數(shù)據(jù)或重要操作的 API,如支付、轉(zhuǎn)賬等,接口簽名提供了額外的安全保障,確保請(qǐng)求的安全性。
  • API 安全合規(guī):在某些行業(yè),如金融、醫(yī)療等,法律法規(guī)可能要求對(duì) API 進(jìn)行嚴(yán)格的安全控制,接口簽名是滿足這些合規(guī)要求的一種方式。

這里有一個(gè)很重要的點(diǎn),就是我們的接口是暴露給對(duì)方服務(wù)端調(diào)用的,而不是暴露給前端調(diào)用的,這樣的場(chǎng)景需要做接口簽名。

二 簽名步驟

一般來(lái)說(shuō),接口簽名的步驟是這樣的:

  • 構(gòu)造待簽名字符串:將請(qǐng)求方法、請(qǐng)求 URI、請(qǐng)求參數(shù)(包括查詢參數(shù)和請(qǐng)求體中的參數(shù))、時(shí)間戳等關(guān)鍵信息按照一定的規(guī)則拼接成待簽名字符串。
  • 生成簽名:使用客戶端持有的私鑰(或密鑰)對(duì)待簽名字符串進(jìn)行加密(或哈希運(yùn)算),生成簽名。
  • 發(fā)送請(qǐng)求:將生成的簽名作為請(qǐng)求的一部分(如請(qǐng)求頭)發(fā)送給服務(wù)器。
  • 驗(yàn)證簽名:服務(wù)器收到請(qǐng)求后,使用相同的規(guī)則構(gòu)造待簽名字符串,并使用對(duì)應(yīng)的公鑰(或密鑰)進(jìn)行驗(yàn)證。如果簽名驗(yàn)證通過(guò),則處理請(qǐng)求;否則,拒絕請(qǐng)求。

實(shí)現(xiàn)接口簽名時(shí),需要注意密鑰管理、時(shí)間戳檢查、錯(cuò)誤處理和日志記錄等安全實(shí)踐。

三 代碼實(shí)踐

接下來(lái),基于 SpringBoot3,松哥來(lái)給大家演示一個(gè)接口簽名案例。

首先我們需要一個(gè)簽名和驗(yàn)簽的工具類。這里我們采用 HmacSHA1 算法。

HmacSHA1 是一種基于 SHA-1 哈希算法的加密哈希消息認(rèn)證碼(Hash-based Message Authentication Code,簡(jiǎn)稱 HMAC)算法。HMAC 是一種用于驗(yàn)證數(shù)據(jù)完整性和認(rèn)證消息發(fā)送者身份的機(jī)制。它結(jié)合了加密哈希函數(shù)和加密密鑰,從而提供了一種安全的方式來(lái)確認(rèn)數(shù)據(jù)的完整性和真實(shí)性。

public class SignUtils {
    /**
     * 使用 HmacSHA1 算法進(jìn)行簽名
     * @param secretKey 密鑰
     * @param data 數(shù)據(jù)
     * @return
     */
    public static String signWithHmacSha1(String secretKey, String data) {
        try {
            SecretKeySpec signingKey = new SecretKeySpec(secretKey.getBytes("UTF-8"), "HmacSHA1");
            Mac mac = Mac.getInstance("HmacSHA1");
            mac.init(signingKey);
            return Base64.getEncoder().encodeToString(mac.doFinal(data.getBytes("UTF-8")));
        } catch (NoSuchAlgorithmException | InvalidKeyException | UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return null;
    }
    /**
     * 驗(yàn)證簽名
     * @param secretKey 密鑰
     * @param data 數(shù)據(jù)
     * @param hmac 簽名
     * @return
     */
    public static boolean verify(String secretKey, String data, String hmac) {
        String calculatedHmac = signWithHmacSha1(secretKey, data);
        return calculatedHmac.equals(hmac);
    }
}

這個(gè)類很簡(jiǎn)單,一個(gè)用來(lái)生成簽名的方法,這個(gè)方法按理說(shuō)可以封裝到 SDK 中給到調(diào)用者,或者告訴調(diào)用者思路,由調(diào)用者自行實(shí)現(xiàn)。

第二個(gè)方法則是一個(gè)簽名驗(yàn)證的方法,對(duì)用戶傳來(lái)的簽名信息進(jìn)行校驗(yàn)。

接下來(lái)我需要一個(gè) App 信息的查詢類:

@Service
public class AppService {
    private static final Map<String, String> APP_INFO = Map.of("app1", "sign1", "app2", "sign2");
    public String getAppKey(String appId) {
        return APP_INFO.get(appId);
    }
}

這個(gè)類的作用是這樣的:比如我們想要接入微信公眾號(hào)后臺(tái),我們需要先在微信公眾號(hào)后臺(tái)配置我們自己的應(yīng)用信息,配置完成后,微信公眾號(hào)會(huì)給我們一個(gè) appId 和 appSecret,微信自己會(huì)把這兩個(gè)信息存入到數(shù)據(jù)庫(kù)中,將來(lái)用戶請(qǐng)求來(lái)的時(shí)候,用戶會(huì)攜帶上 appId,但是不會(huì)攜帶 appSecret,微信公眾號(hào)可以根據(jù)用戶攜帶的 appId 去數(shù)據(jù)庫(kù)中查詢到 appSecret,然后進(jìn)行驗(yàn)簽。

松哥這個(gè)案例簡(jiǎn)化了,直接模擬了兩個(gè) appId 和 appSecret 存入到 Map 中,這里提供一個(gè)根據(jù) appId 查詢 appSecret 的函數(shù)。

接下來(lái)我們定義一個(gè)攔截器,在攔截器中對(duì)簽名進(jìn)行驗(yàn)證:

public class SignInterceptor implements HandlerInterceptor {
    public final static Logger logger = LoggerFactory.getLogger(SignInterceptor.class);
    AppService appService;
    public SignInterceptor(AppService appService) {
        this.appService = appService;
    }
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String appId = request.getHeader("appId");
        String timestamp = request.getHeader("timestamp");
        String sign = request.getHeader("sign");
        if (StringUtils.hasText(appId) && StringUtils.hasText(timestamp) && StringUtils.hasText(sign)) {
            if (LocalDateTime.now().compareTo(LocalDateTime.ofInstant(Instant.ofEpochMilli(Long.parseLong(timestamp)), ZoneId.systemDefault()).plusMinutes(1L)) < 0) {
                String originalSign = appId + "-" + appService.getAppKey(appId) + "-" + timestamp;
                if (SignUtils.verify(appService.getAppKey(appId), originalSign, sign)) {
                    return true;
                } else {
                    logger.error("簽名驗(yàn)證失敗");
                }
            } else {
                logger.error("簽名已過(guò)期");
            }
        } else {
            logger.error("簽名信息不完整");
        }
        response.setStatus(401);
        return false;
    }
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
}

請(qǐng)求頭中主要有三個(gè)信息:

  • 應(yīng)用 id
  • 時(shí)間戳
  • 生成的簽名

我們先從請(qǐng)求頭中取出來(lái)這三個(gè)信息,檢查是否為空;

然后判斷一下這個(gè)時(shí)間戳,要求必須是 1 分鐘之內(nèi)的請(qǐng)求,這個(gè)判斷目的主要是為了防止重放攻擊。

接下來(lái),根據(jù) appId,以及根據(jù) appId 查詢出來(lái)的 appSecret,以及 timestamp,組成一個(gè)字符串,調(diào)用驗(yàn)簽方法進(jìn)行驗(yàn)證,如果驗(yàn)證通過(guò),就說(shuō)明請(qǐng)求沒(méi)問(wèn)題。

最后我們配置一下,讓這個(gè)攔截器生效:

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    AppService appService;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new SignInterceptor(appService))
                //只攔截需要接口驗(yàn)簽的請(qǐng)求
                .addPathPatterns("/app/**");
    }
}

OK,大功告成,接下來(lái)就可以寫(xiě)接口進(jìn)行測(cè)試了:

@RestController
public class UserController {
    @GetMapping("/hello")
    public String hello() {
        return "hello";
    }
}

至于調(diào)用方要如何生成簽名呢?松哥也給大家一個(gè)例子:

@Autowired
AppService appService;
@Test
void contextLoads() {
    String appId = "app1";
    long timeMillis = System.currentTimeMillis();
    String appSecret = appService.getAppKey(appId);
    String sign = SignUtils.signWithHmacSha1(appSecret, appId + "-" + appSecret + "-" + timeMillis);
    System.out.println("timeMillis = " + timeMillis);
    System.out.println("sign = " + sign);
}

appId 和 appSecret 則是對(duì)方從我們這里申請(qǐng)得到的。

postman 上測(cè)試時(shí),類似這樣:

到此這篇關(guān)于SpringBoot3實(shí)戰(zhàn):實(shí)現(xiàn)接口簽名驗(yàn)證的文章就介紹到這了,更多相關(guān)SpringBoot接口簽名驗(yàn)證內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • MyBatis緩存實(shí)現(xiàn)原理及代碼實(shí)例解析

    MyBatis緩存實(shí)現(xiàn)原理及代碼實(shí)例解析

    這篇文章主要介紹了MyBatis緩存實(shí)現(xiàn)原理及代碼實(shí)例解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-08-08
  • eclipse創(chuàng)建一個(gè)基于maven的web項(xiàng)目詳細(xì)步驟

    eclipse創(chuàng)建一個(gè)基于maven的web項(xiàng)目詳細(xì)步驟

    開(kāi)始學(xué)習(xí)maven,并用maven創(chuàng)建了第一個(gè)屬于自己的web項(xiàng)目,下面這篇文章主要給大家介紹了關(guān)于eclipse創(chuàng)建一個(gè)基于maven的web項(xiàng)目的相關(guān)資料,文中通過(guò)圖文介紹的非常詳細(xì),需要的朋友可以參考下
    2023-12-12
  • SpringCloud?eureka(server)微服務(wù)集群搭建過(guò)程

    SpringCloud?eureka(server)微服務(wù)集群搭建過(guò)程

    這篇文章主要介紹了微服務(wù)SpringCloud-eureka(server)集群搭建,?項(xiàng)目搭建的主要步驟和配置就是創(chuàng)建項(xiàng)目和引入pom依賴,本文通過(guò)圖文示例代碼相結(jié)合給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2022-07-07
  • SpringBoot中使用@Async注解失效場(chǎng)景及說(shuō)明

    SpringBoot中使用@Async注解失效場(chǎng)景及說(shuō)明

    在Spring?Boot中,@Async注解就像一把刀,能幫你輕松處理那些耗時(shí)的任務(wù),讓主線程可以繼續(xù)忙別的事兒,不過(guò),跟所有強(qiáng)大的工具一樣,用不好它也可能出岔子,為了避免這些坑,咱們得深入了解下@Async注解,接下來(lái),咱們就來(lái)聊聊7種常見(jiàn)的@Async失效情況,需要的朋友可以參考下
    2024-07-07
  • JavaWeb 簡(jiǎn)單分頁(yè)實(shí)現(xiàn)代碼

    JavaWeb 簡(jiǎn)單分頁(yè)實(shí)現(xiàn)代碼

    這篇文章主要介紹了JavaWeb 簡(jiǎn)單分頁(yè)實(shí)現(xiàn)代碼的相關(guān)資料,需要的朋友可以參考下
    2016-11-11
  • 如何擴(kuò)展Spring Cache實(shí)現(xiàn)支持多級(jí)緩存

    如何擴(kuò)展Spring Cache實(shí)現(xiàn)支持多級(jí)緩存

    這篇文章主要介紹了如何擴(kuò)展Spring Cache實(shí)現(xiàn)支持多級(jí)緩存,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-11-11
  • java聯(lián)系人管理系統(tǒng)簡(jiǎn)單設(shè)計(jì)

    java聯(lián)系人管理系統(tǒng)簡(jiǎn)單設(shè)計(jì)

    這篇文章主要為大家詳細(xì)介紹了java聯(lián)系人管理系統(tǒng)簡(jiǎn)單設(shè)計(jì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-10-10
  • 利用Spring Session和redis對(duì)Session進(jìn)行共享詳解

    利用Spring Session和redis對(duì)Session進(jìn)行共享詳解

    這篇文章主要給大家介紹了關(guān)于利用Spring、Session和redis對(duì)Session進(jìn)行共享的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-09-09
  • Spring中的父子容器原理解析

    Spring中的父子容器原理解析

    這篇文章主要為大家介紹了Spring中的父子容器原理解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-07-07
  • 日歷顯示讀出輸入的年月的java代碼

    日歷顯示讀出輸入的年月的java代碼

    這篇文章主要介紹了日歷顯示讀出輸入的年月的java代碼,有需要的朋友可以參考一下
    2013-12-12

最新評(píng)論