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

SpringBoot3基于 Sa-Token 實現(xiàn) API 接口簽名校驗實戰(zhàn)

 更新時間:2025年12月26日 08:25:13   作者:九月生  
本文介紹了基于Spring Boot 3和Sa-Token框架實現(xiàn)API接口簽名校驗的解決方案,通過sa-token-sign模塊提供開箱即用的簽名校驗功能,下面就來詳細的介紹一下,感興趣的可以了解一下

在微服務架構中,系統(tǒng)之間的調(diào)用往往需要保證 安全性。如果缺乏有效的防護機制,接口極易遭受偽造請求攻擊。
接口簽名校驗 就是一種常見的安全手段,可以有效避免參數(shù)篡改、重放攻擊。
本文將基于 Spring Boot 3 + Sa-Tokensa-token-sign 模塊,手把手帶你實現(xiàn)接口簽名校驗,并擴展到數(shù)據(jù)庫存儲,支持動態(tài)接入。

簽名校驗流程圖

   Client                              Server
      |                                    |
      | appid, params, sign, timestamp, nonce |
      | -----------------------------------> |
      |                                    | 1. 從數(shù)據(jù)庫加載密鑰配置
      |                                    | 2. 驗證 timestamp 是否在有效期內(nèi)
      |                                    | 3. 驗證 nonce 是否重復
      |                                    | 4. 計算簽名并比對
      |                                    | 5. Redis 緩存配置(12小時)
      | <----------------------------------|
      |           Response                 |

Sa-Token 簽名模塊簡介

sa-token-sign 模塊開箱即用,提供了:

? 支持 MD5 / SHA256 / SHA512

? 內(nèi)置 timestamp / nonce 校驗

? 支持 多應用配置

? 提供 @SaCheckSign 注解,零侵入接入

? 支持 自定義配置源(數(shù)據(jù)庫)

項目依賴

<!-- Sa-Token Starter -->
<dependency>
    <groupId>cn.dev33</groupId>
    <artifactId>sa-token-spring-boot3-starter</artifactId>
    <version>1.44.0</version>
</dependency>

<!-- API 參數(shù)簽名 -->
<dependency>
    <groupId>cn.dev33</groupId>
    <artifactId>sa-token-sign</artifactId>
    <version>1.44.0</version>
</dependency>

<!-- Sa-Token 與 Redis 集成(用于緩存簽名配置) -->
<dependency>
    <groupId>cn.dev33</groupId>
    <artifactId>sa-token-redis-template</artifactId>
    <version>1.44.0</version>
</dependency>

數(shù)據(jù)庫設計

1 表結(jié)構

create table t_app_sign_config
(
    id                  bigint auto_increment comment '主鍵ID' primary key,
    app_id              varchar(64)  not null comment '應用ID',
    secret_key          varchar(128) not null comment '密鑰',
    digest_algo         varchar(32)  default 'md5' not null comment '簽名算法: md5 / sha256 / sha512',
    timestamp_disparity bigint       default 900000 null comment '時間戳允許誤差(毫秒) 默認15分鐘',
    create_by           varchar(50)  null comment '創(chuàng)建人',
    create_time         datetime     default CURRENT_TIMESTAMP null comment '創(chuàng)建時間',
    update_by           varchar(50)  null comment '更新人',
    update_time         datetime     default CURRENT_TIMESTAMP null on update CURRENT_TIMESTAMP comment '更新時間',
    constraint uk_app_id unique (app_id)
) comment '應用簽名配置表';

2 初始化數(shù)據(jù)

INSERT INTO t_app_sign_config (app_id, secret_key, digest_algo, timestamp_disparity, create_by)
VALUES ('AppId1', '601b8ddd3037c782476e4be8102f6a07', 'md5', 900000, 'admin');

INSERT INTO t_app_sign_config (app_id, secret_key, digest_algo, timestamp_disparity, create_by)
VALUES ('AppId2', '954911e93f7e14fe1e09a713bf96b0da', 'md5', 900000, 'admin');

后端實現(xiàn)

1 實體類

@Data
@EqualsAndHashCode(callSuper = true)
@TableName("t_app_sign_config")
public class AppSignConfig extends BaseEntity {

    /**
     * 應用ID
     */
    private String appId;

    /**
     * 密鑰
     */
    private String secretKey;

    /**
     * md5 / sha256 / sha512
     */
    private String digestAlgo;

    /**
     * 時間戳誤差(秒)
     */
    private Long timestampDisparity;
}

2 Service 層(含 Redis 緩存)

public interface IAppSignConfigService extends IService<AppSignConfig> {
    AppSignConfig getByAppId(String appId);
}
@Service
public class AppSignConfigServiceImpl extends ServiceImpl<AppSignConfigRepository, AppSignConfig>
        implements IAppSignConfigService {

    private static final String CACHE_PREFIX = "rbom-sync:api:signconfig:";

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Override
    public AppSignConfig getByAppId(String appId) {
        String cacheKey = CACHE_PREFIX + appId;
        String cache = redisTemplate.opsForValue().get(cacheKey);
        if (cache != null) {
            return JsonUtils.fromJson(cache, AppSignConfig.class);
        }
        AppSignConfig appSignConfig = lambdaQuery()
                .eq(AppSignConfig::getAppId, appId)
                .one();
        // 緩存配置(12小時)
        redisTemplate.opsForValue()
                .set(cacheKey, JsonUtils.toJson(appSignConfig), Duration.ofHours(12));
        return appSignConfig;
    }
}

3 自定義簽名配置加載器

為什么能夠動態(tài)加載簽名信息,這一步很重要

@Component
public class MySignConfigLoader {

    private static final Logger logger = LoggerFactory.getLogger(MySignConfigLoader.class);

    @Autowired
    private IAppSignConfigService appSignConfigService;

    @PostConstruct
    public void init() {
        logger.info("初始化自定義簽名配置加載器...");
        // 覆蓋 SaSignMany 的查找邏輯
        SaSignMany.findSaSignConfigMethod = (appid) -> {
            try {
                logger.debug("查找應用簽名配置,appid: {}", appid);
                AppSignConfig appSignConfig = appSignConfigService.getByAppId(appid);

                if (appSignConfig == null) {
                    logger.warn("未找到應用簽名配置,appid: {}", appid);
                    throw new RuntimeException("appid 不存在: " + appid);
                }
                SaSignConfig config = new SaSignConfig();
                config.setSecretKey(appSignConfig.getSecretKey());
                config.setDigestAlgo(appSignConfig.getDigestAlgo());
                config.setTimestampDisparity(appSignConfig.getTimestampDisparity());
                logger.debug("成功加載應用簽名配置,appid: {}, algorithm: {}",
                        appid, appSignConfig.getDigestAlgo());
                return config;
            } catch (Exception e) {
                logger.error("加載應用簽名配置失敗,appid: {}, error: {}", appid, e.getMessage(), e);
                throw new BusinessException("加載應用簽名配置失敗: " + e.getMessage(), e);
            }
        };
        logger.info("自定義簽名配置加載器初始化完成");
    }
}

4 攔截器配置

@Configuration
public class SaTokenConfigure implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 開啟 Sa-Token 注解攔截
        registry.addInterceptor(new SaInterceptor()).addPathPatterns("/**");
    }
}

5 Controller 使用

@RestController
@RequestMapping("/sync")
public class SyncController {

    @Autowired
    private IModelService modelService;

    // 從請求參數(shù)中動態(tài)獲取 appid
    @SaCheckSign(appid = "#{appid}")
    @GetMapping("/model")
    public ResponseEntity<Res> model() {
        List<Model> models = modelService.list();
        return ResponseEntity.ok(Res.success(models));
    }
}

6 application.properties 配置

# Sa-Token 配置
sa-token.token-name=***-sync:satoken

客戶端調(diào)用示例

1 Java 客戶端(OkHttp)

OkHttpClient client = new OkHttpClient().newBuilder().build();

String appid = "AppId1";
String nonce = UUID.randomUUID().toString();
long timestamp = System.currentTimeMillis();

// 拼接簽名字符串
String raw = "appid=" + appid + "&nonce=" + nonce + "&timestamp=" + timestamp
             + "&key=601b8ddd3037c782476e4be8102f6a07";
// 生成 MD5 簽名
String sign = DigestUtils.md5DigestAsHex(raw.getBytes(StandardCharsets.UTF_8));

// 發(fā)送請求
Request request = new Request.Builder()
        .url("http://***:8080/sync/model"
             + "?appid=" + appid
             + "&sign=" + sign
             + "&nonce=" + nonce
             + "&timestamp=" + timestamp)
        .get()
        .build();

Response response = client.newCall(request).execute();
System.out.println(response.body().string());

6.2 curl 示例

curl "http://***:8080/sync/model?appid=MDM&sign=***&nonce=***&timestamp=***"

架構設計亮點

設計點說明
數(shù)據(jù)庫存儲密鑰配置持久化,支持動態(tài)擴展新應用
Redis 緩存緩存簽名配置 12 小時,減少數(shù)據(jù)庫查詢
自定義加載器通過 @PostConstruct 覆蓋默認配置加載邏輯
動態(tài) appid使用 SpEL 表達式 #{appid} 從請求參數(shù)獲取
異常處理統(tǒng)一的日志記錄和異常封裝
唯一約束數(shù)據(jù)庫 app_id 唯一索引防止重復

常見問題

Q1: 如何添加新的應用?

直接在數(shù)據(jù)庫 t_app_sign_config 表插入新記錄即可,無需重啟服務。

Q2: 緩存失效后如何刷新?

緩存 TTL 為 12 小時,過期后自動從數(shù)據(jù)庫重新加載。

Q3: timestamp_disparity 的含義?

表示時間戳允許的誤差范圍(毫秒),默認 900000ms(15 分鐘),防止重放攻擊。

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

相關文章

  • Java基于redis實現(xiàn)分布式鎖代碼實例

    Java基于redis實現(xiàn)分布式鎖代碼實例

    這篇文章主要介紹了Java基于redis實現(xiàn)分布式鎖代碼實例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-04-04
  • 程序員最喜歡的ThreadLocal使用姿勢

    程序員最喜歡的ThreadLocal使用姿勢

    ThreadLocal并不是一個Thread,而是Thread的局部變量,也許把它命名為ThreadLocalVariable更容易讓人理解一些,下面這篇文章主要給大家介紹了程序員最喜歡的ThreadLocal使用姿勢,需要的朋友可以參考下
    2022-02-02
  • 微服務和分布式的區(qū)別詳解

    微服務和分布式的區(qū)別詳解

    在本篇文章里小編給各位整理了關于微服務和分布式的區(qū)別以及相關知識點總結(jié),有興趣的朋友們學習下。
    2019-07-07
  • Java中八種基本數(shù)據(jù)類型的默認值

    Java中八種基本數(shù)據(jù)類型的默認值

    這篇文章主要介紹了Java中八種基本數(shù)據(jù)類型的默認值 的相關資料,需要的朋友可以參考下
    2016-07-07
  • RocketMq同組消費者如何自動設置InstanceName

    RocketMq同組消費者如何自動設置InstanceName

    這篇文章主要介紹了RocketMq同組消費者如何自動設置InstanceName問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-06-06
  • 淺析Java內(nèi)存模型與垃圾回收

    淺析Java內(nèi)存模型與垃圾回收

    下面小編就為大家?guī)硪黄獪\析Java內(nèi)存模型與垃圾回收。小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧,祝大家游戲愉快哦
    2016-05-05
  • springboot單獨使用feign簡化接口調(diào)用方式

    springboot單獨使用feign簡化接口調(diào)用方式

    這篇文章主要介紹了springboot單獨使用feign簡化接口調(diào)用方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • vue3實現(xiàn)一個todo-list

    vue3實現(xiàn)一個todo-list

    這篇文章主要為大家詳細介紹了基于vuejs實現(xiàn)一個todolist項目,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能給你帶來幫助
    2021-08-08
  • Java注解如何基于Redission實現(xiàn)分布式鎖

    Java注解如何基于Redission實現(xiàn)分布式鎖

    這篇文章主要介紹了Java注解如何基于Redission實現(xiàn)分布式鎖,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-01-01
  • SpringBoot 項目中的圖片處理策略之本地存儲與路徑映射

    SpringBoot 項目中的圖片處理策略之本地存儲與路徑映射

    在SpringBoot項目中,靜態(tài)資源存放在static目錄下,使得前端可以通過URL來訪問這些資源,我們就需要將文件系統(tǒng)的文件路徑與URL建立一個映射關系,把文件系統(tǒng)中的文件當成我們的靜態(tài)資源即可,本文給大家介紹SpringBoot本地存儲與路徑映射的相關知識,感興趣的朋友一起看看吧
    2023-12-12

最新評論

长沙县| 玉门市| 余姚市| 滦南县| 青州市| 安顺市| 长白| 光山县| 松原市| 登封市| 黑河市| 喀喇| 昔阳县| 乌拉特后旗| 蒙自县| 大余县| 威宁| 卓资县| 云南省| 巫山县| 滨州市| 璧山县| 上高县| 庆阳市| 江源县| 犍为县| 寻甸| 五原县| 中牟县| 花垣县| 长汀县| 阿拉尔市| 丽江市| 洱源县| 固安县| 什邡市| 洛扎县| 左云县| 开平市| 贵定县| 汾阳市|