最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

有時候我們要把自己的服務暴露給第三方去調用,為了防止接口不被授權訪問,我們一般采用接口簽名的方式去保護接口。

接下來松哥和大家聊一聊這個話題。

一 場景分析

什么時候需要接口簽名?

接口簽名是一種重要的安全機制,用于確保 API 請求的真實性、數據的完整性以及防止重放攻擊。

當我們需要保護 API 接口不被未授權訪問、確保傳輸數據在過程中不被篡改,或者需要防止惡意用戶利用 API 進行攻擊時,就需要使用接口簽名。

松哥來舉幾個需要做接口簽名的例子:

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

這里有一個很重要的點,就是我們的接口是暴露給對方服務端調用的,而不是暴露給前端調用的,這樣的場景需要做接口簽名。

二 簽名步驟

一般來說,接口簽名的步驟是這樣的:

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

實現接口簽名時,需要注意密鑰管理、時間戳檢查、錯誤處理和日志記錄等安全實踐。

三 代碼實踐

接下來,基于 SpringBoot3,松哥來給大家演示一個接口簽名案例。

首先我們需要一個簽名和驗簽的工具類。這里我們采用 HmacSHA1 算法。

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

public class SignUtils {
    /**
     * 使用 HmacSHA1 算法進行簽名
     * @param secretKey 密鑰
     * @param data 數據
     * @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;
    }
    /**
     * 驗證簽名
     * @param secretKey 密鑰
     * @param data 數據
     * @param hmac 簽名
     * @return
     */
    public static boolean verify(String secretKey, String data, String hmac) {
        String calculatedHmac = signWithHmacSha1(secretKey, data);
        return calculatedHmac.equals(hmac);
    }
}

這個類很簡單,一個用來生成簽名的方法,這個方法按理說可以封裝到 SDK 中給到調用者,或者告訴調用者思路,由調用者自行實現。

第二個方法則是一個簽名驗證的方法,對用戶傳來的簽名信息進行校驗。

接下來我需要一個 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);
    }
}

這個類的作用是這樣的:比如我們想要接入微信公眾號后臺,我們需要先在微信公眾號后臺配置我們自己的應用信息,配置完成后,微信公眾號會給我們一個 appId 和 appSecret,微信自己會把這兩個信息存入到數據庫中,將來用戶請求來的時候,用戶會攜帶上 appId,但是不會攜帶 appSecret,微信公眾號可以根據用戶攜帶的 appId 去數據庫中查詢到 appSecret,然后進行驗簽。

松哥這個案例簡化了,直接模擬了兩個 appId 和 appSecret 存入到 Map 中,這里提供一個根據 appId 查詢 appSecret 的函數。

接下來我們定義一個攔截器,在攔截器中對簽名進行驗證:

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("簽名驗證失敗");
                }
            } else {
                logger.error("簽名已過期");
            }
        } 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);
    }
}

請求頭中主要有三個信息:

  • 應用 id
  • 時間戳
  • 生成的簽名

我們先從請求頭中取出來這三個信息,檢查是否為空;

然后判斷一下這個時間戳,要求必須是 1 分鐘之內的請求,這個判斷目的主要是為了防止重放攻擊。

接下來,根據 appId,以及根據 appId 查詢出來的 appSecret,以及 timestamp,組成一個字符串,調用驗簽方法進行驗證,如果驗證通過,就說明請求沒問題。

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

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

OK,大功告成,接下來就可以寫接口進行測試了:

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

至于調用方要如何生成簽名呢?松哥也給大家一個例子:

@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 則是對方從我們這里申請得到的。

postman 上測試時,類似這樣:

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

相關文章

  • java中將科學計數法轉換普通計數法的簡單方法

    java中將科學計數法轉換普通計數法的簡單方法

    下面小編就為大家?guī)硪黄猨ava中將科學計數法轉換普通計數法的簡單方法。小編覺得挺不錯的,現在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-12-12
  • Java對MySQL數據庫進行連接、查詢和修改操作方法

    Java對MySQL數據庫進行連接、查詢和修改操作方法

    這篇文章主要介紹了Java對MySQL數據庫進行連接、查詢和修改操作方法,需要的朋友可以參考下
    2017-07-07
  • 解決Spring boot 整合Junit遇到的坑

    解決Spring boot 整合Junit遇到的坑

    這篇文章主要介紹了解決Spring boot 整合Junit遇到的坑,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • maven依賴的配置和排除依賴過程

    maven依賴的配置和排除依賴過程

    這篇文章主要介紹了maven依賴的配置和排除依賴過程,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2025-03-03
  • Kafka在客戶端實現消息的發(fā)送與讀取

    Kafka在客戶端實現消息的發(fā)送與讀取

    這篇文章主要介紹了Kafka在客戶端實現消息的發(fā)送與讀取,KafkaProducer是用于發(fā)送消息的類,ProducerRecord類用于封裝Kafka的消息,KafkaProducer的實例化需要指定的參數,Producer的參數定義在 org.apache.kafka.clients.producer.ProducerConfig類中,需要的朋友可以參考下
    2023-12-12
  • Java wait和notify虛假喚醒原理

    Java wait和notify虛假喚醒原理

    這篇文章主要介紹了Java wait和notify虛假喚醒,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-04-04
  • SpringBoot如何集成Kafka低版本和高版本

    SpringBoot如何集成Kafka低版本和高版本

    這篇文章主要介紹了SpringBoot如何集成Kafka低版本和高版本問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-03-03
  • Mybatis動態(tài)SQL之where標簽用法說明

    Mybatis動態(tài)SQL之where標簽用法說明

    這篇文章主要介紹了Mybatis動態(tài)SQL之where標簽用法說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • SpringBoot Tomcat啟動實例代碼詳解

    SpringBoot Tomcat啟動實例代碼詳解

    這篇文章主要介紹了SpringBoot Tomcat啟動實例代碼詳解,需要的朋友可以參考下
    2017-09-09
  • idea中不用git解除關聯(lián)的方式

    idea中不用git解除關聯(lián)的方式

    IntelliJ?IDEA是一款高效的Java編程環(huán)境,提供智能編碼輔助、J2EE、Ant等集成,支持本地和遠程調試,本文講述了如何在IDEA中解除版本控制和刪除.git文件夾,幫助開發(fā)者更好地管理項目設置和提升開發(fā)效率
    2024-10-10

最新評論

汕头市| 庆城县| 朝阳区| 枞阳县| 丰镇市| 横山县| 常宁市| 邻水| 格尔木市| 永安市| 漯河市| 尉氏县| 东阳市| 雅安市| 石楼县| 阳春市| 从化市| 修武县| 赣州市| 南城县| 南丹县| 青铜峡市| 溆浦县| 桃园市| 临洮县| 浦东新区| 邻水| 宜川县| 罗江县| 武川县| 米泉市| 安阳市| 四平市| 怀化市| 武穴市| 五常市| 伊通| 大同市| 石阡县| 金昌市| 册亨县|