From fab7f200ccb97f580ef17bdcb860ea8d9ef16b58 Mon Sep 17 00:00:00 2001 From: click33 <2393584716@qq.com> Date: Thu, 10 Apr 2025 20:38:47 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20API=20Key=20=E6=A8=A1=E5=9D=97=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=20namespace=20=E5=91=BD=E5=90=8D=E7=A9=BA=E9=97=B4?= =?UTF-8?q?=EF=BC=8C=E4=B8=BA=E5=A4=9A=E8=B4=A6=E5=8F=B7=E6=A8=A1=E5=BC=8F?= =?UTF-8?q?=E6=8F=90=E4=BE=9B=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../satoken/apikey/SaApiKeyTemplate.java | 57 ++++++++++++------- .../apikey/loader/SaApiKeyDataLoader.java | 3 +- .../com/pj/mock/SaApiKeyDataLoaderImpl.java | 2 +- .../java/com/pj/mock/SaApiKeyMockMapper.java | 4 +- .../com/pj/SaTokenJwtDemoApplication.java | 2 +- sa-token-doc/plugin/api-key.md | 25 +++++++- 6 files changed, 66 insertions(+), 27 deletions(-) diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/apikey/SaApiKeyTemplate.java b/sa-token-core/src/main/java/cn/dev33/satoken/apikey/SaApiKeyTemplate.java index 64328b95..7e721c56 100644 --- a/sa-token-core/src/main/java/cn/dev33/satoken/apikey/SaApiKeyTemplate.java +++ b/sa-token-core/src/main/java/cn/dev33/satoken/apikey/SaApiKeyTemplate.java @@ -41,20 +41,41 @@ import java.util.List; */ public class SaApiKeyTemplate { + /** + *默认命名空间 + */ + public static final String DEFAULT_NAMESPACE = "apikey"; + + /** + * 命名空间 + */ + public String namespace; + /** * Raw Session 读写委托 */ - public SaRawSessionDelegator rawSessionDelegator = new SaRawSessionDelegator("apikey"); + public SaRawSessionDelegator rawSessionDelegator; /** * 在 raw-session 中的保存索引列表使用的 key */ public static final String API_KEY_LIST = "__HD_API_KEY_LIST"; + public SaApiKeyTemplate(){ + this(DEFAULT_NAMESPACE); + } + /** - * 网络传输时的参数名称 (字母全小写) + * 实例化 + * @param namespace 命名空间,用于多实例隔离 */ - public static final String API_KEY_PARAMETER_NAME = "apikey"; + public SaApiKeyTemplate(String namespace){ + if(SaFoxUtil.isEmpty(namespace)) { + throw new ApiKeyException("namespace 不能为空"); + } + this.namespace = namespace; + this.rawSessionDelegator = new SaRawSessionDelegator(namespace); + } // ------------------- ApiKey @@ -73,7 +94,7 @@ public class SaApiKeyTemplate { * @return / */ public ApiKeyModel getApiKeyModelFromDatabase(String apiKey) { - return SaManager.getSaApiKeyDataLoader().getApiKeyModelFromDatabase(apiKey); + return SaManager.getSaApiKeyDataLoader().getApiKeyModelFromDatabase(namespace, apiKey); } /** @@ -133,7 +154,7 @@ public class SaApiKeyTemplate { } // 记录索引 - if (SaManager.getSaApiKeyDataLoader().getIsRecordIndex()) { + if (getIsRecordIndex()) { // 添加索引 SaSession session = rawSessionDelegator.getSessionById(ak.getLoginId()); ArrayList apiKeyList = session.get(API_KEY_LIST, ArrayList::new); @@ -170,7 +191,7 @@ public class SaApiKeyTemplate { getSaTokenDao().deleteObject(splicingApiKeySaveKey(apiKey)); // 删索引 - if(SaManager.getSaApiKeyDataLoader().getIsRecordIndex()) { + if(getIsRecordIndex()) { // RawSession 中不存在,提前退出 SaSession session = rawSessionDelegator.getSessionById(ak.getLoginId(), false); if(session == null) { @@ -199,7 +220,7 @@ public class SaApiKeyTemplate { */ public void deleteApiKeyByLoginId(Object loginId) { // 先判断是否开启索引 - if(! SaManager.getSaApiKeyDataLoader().getIsRecordIndex()) { + if(! getIsRecordIndex()) { SaManager.getLog().warn("当前 API Key 模块未开启索引记录功能,无法执行 deleteApiKeyByLoginId 操作"); return; } @@ -375,7 +396,7 @@ public class SaApiKeyTemplate { */ public void adjustIndex(Object loginId, SaSession session) { // 先判断是否开启索引 - if(! SaManager.getSaApiKeyDataLoader().getIsRecordIndex()) { + if(! getIsRecordIndex()) { SaManager.getLog().warn("当前 API Key 模块未开启索引记录功能,无法执行 adjustIndex 操作"); return; } @@ -431,7 +452,7 @@ public class SaApiKeyTemplate { */ public List getApiKeyList(Object loginId) { // 先判断是否开启索引 - if(! SaManager.getSaApiKeyDataLoader().getIsRecordIndex()) { + if(! getIsRecordIndex()) { SaManager.getLog().warn("当前 API Key 模块未开启索引记录功能,无法执行 getApiKeyList 操作"); return new ArrayList<>(); } @@ -464,13 +485,13 @@ public class SaApiKeyTemplate { public String readApiKeyValue(SaRequest request) { // 优先从请求参数中获取 - String apiKey = request.getParam(API_KEY_PARAMETER_NAME); + String apiKey = request.getParam(namespace); if(SaFoxUtil.isNotEmpty(apiKey)) { return apiKey; } // 然后请求头 - apiKey = request.getHeader(API_KEY_PARAMETER_NAME); + apiKey = request.getHeader(namespace); if(SaFoxUtil.isNotEmpty(apiKey)) { return apiKey; } @@ -496,7 +517,6 @@ public class SaApiKeyTemplate { } - // ------------------- 拼接key /** @@ -505,7 +525,7 @@ public class SaApiKeyTemplate { * @return key */ public String splicingApiKeySaveKey(String apiKey) { - return getSaTokenConfig().getTokenName() + ":apikey:" + apiKey; + return getSaTokenConfig().getTokenName() + ":" + namespace + ":" + apiKey; } @@ -530,13 +550,10 @@ public class SaApiKeyTemplate { } /** - * 校验是否开启了索引记录功能,如果未开启则抛出异常 + * 是否保存索引信息 */ -// protected void checkOpenRecordIndex() { -// if(! SaManager.getSaApiKeyDataLoader().getIsRecordIndex()) { -// SaManager.getLog().warn("当前 API Key 模块未开启索引记录功能,无法执行此操作"); -// throw new ApiKeyException("当前 API Key 模块未开启索引记录功能,无法执行此操作").setCode(SaErrorCode.CODE_12305); -// } -// } + public boolean getIsRecordIndex() { + return SaManager.getSaApiKeyDataLoader().getIsRecordIndex(); + } } diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/apikey/loader/SaApiKeyDataLoader.java b/sa-token-core/src/main/java/cn/dev33/satoken/apikey/loader/SaApiKeyDataLoader.java index 7a877f00..5d81936d 100644 --- a/sa-token-core/src/main/java/cn/dev33/satoken/apikey/loader/SaApiKeyDataLoader.java +++ b/sa-token-core/src/main/java/cn/dev33/satoken/apikey/loader/SaApiKeyDataLoader.java @@ -38,10 +38,11 @@ public interface SaApiKeyDataLoader { /** * 根据 apiKey 从数据库获取 ApiKeyModel 信息 (实现此方法无需为数据做缓存处理,框架内部已包含缓存逻辑) * + * @param namespace / * @param apiKey / * @return ApiKeyModel */ - default ApiKeyModel getApiKeyModelFromDatabase(String apiKey) { + default ApiKeyModel getApiKeyModelFromDatabase(String namespace, String apiKey) { return null; } diff --git a/sa-token-demo/sa-token-demo-apikey/src/main/java/com/pj/mock/SaApiKeyDataLoaderImpl.java b/sa-token-demo/sa-token-demo-apikey/src/main/java/com/pj/mock/SaApiKeyDataLoaderImpl.java index c36cabaf..6e56341f 100644 --- a/sa-token-demo/sa-token-demo-apikey/src/main/java/com/pj/mock/SaApiKeyDataLoaderImpl.java +++ b/sa-token-demo/sa-token-demo-apikey/src/main/java/com/pj/mock/SaApiKeyDataLoaderImpl.java @@ -24,7 +24,7 @@ public class SaApiKeyDataLoaderImpl implements SaApiKeyDataLoader { // 根据 apiKey 从数据库获取 ApiKeyModel 信息 (实现此方法无需为数据做缓存处理,框架内部已包含缓存逻辑) @Override - public ApiKeyModel getApiKeyModelFromDatabase(String apiKey) { + public ApiKeyModel getApiKeyModelFromDatabase(String namespace, String apiKey) { return apiKeyMockMapper.getApiKeyModel(apiKey); } diff --git a/sa-token-demo/sa-token-demo-apikey/src/main/java/com/pj/mock/SaApiKeyMockMapper.java b/sa-token-demo/sa-token-demo-apikey/src/main/java/com/pj/mock/SaApiKeyMockMapper.java index 65205dc6..6d37f78d 100644 --- a/sa-token-demo/sa-token-demo-apikey/src/main/java/com/pj/mock/SaApiKeyMockMapper.java +++ b/sa-token-demo/sa-token-demo-apikey/src/main/java/com/pj/mock/SaApiKeyMockMapper.java @@ -1,7 +1,7 @@ package com.pj.mock; import cn.dev33.satoken.apikey.model.ApiKeyModel; -import org.springframework.stereotype.Service; +import org.springframework.stereotype.Component; import java.util.HashMap; import java.util.Map; @@ -12,7 +12,7 @@ import java.util.Map; * @author click33 * @since 2025/4/4 */ -@Service +@Component public class SaApiKeyMockMapper { // 添加模拟测试数据 diff --git a/sa-token-demo/sa-token-demo-jwt/src/main/java/com/pj/SaTokenJwtDemoApplication.java b/sa-token-demo/sa-token-demo-jwt/src/main/java/com/pj/SaTokenJwtDemoApplication.java index 65a76bb0..b86a7394 100644 --- a/sa-token-demo/sa-token-demo-jwt/src/main/java/com/pj/SaTokenJwtDemoApplication.java +++ b/sa-token-demo/sa-token-demo-jwt/src/main/java/com/pj/SaTokenJwtDemoApplication.java @@ -11,5 +11,5 @@ public class SaTokenJwtDemoApplication { SpringApplication.run(SaTokenJwtDemoApplication.class, args); System.out.println("\n启动成功:Sa-Token配置如下:" + SaManager.getConfig()); } - + } \ No newline at end of file diff --git a/sa-token-doc/plugin/api-key.md b/sa-token-doc/plugin/api-key.md index 1531ab4f..6e1a504b 100644 --- a/sa-token-doc/plugin/api-key.md +++ b/sa-token-doc/plugin/api-key.md @@ -223,7 +223,7 @@ public class SaApiKeyDataLoaderImpl implements SaApiKeyDataLoader { // 根据 apiKey 从数据库获取 ApiKeyModel 信息 (实现此方法无需为数据做缓存处理,框架内部已包含缓存逻辑) @Override - public ApiKeyModel getApiKeyModelFromDatabase(String apiKey) { + public ApiKeyModel getApiKeyModelFromDatabase(String namespace, String apiKey) { return apiKeyMapper.getApiKeyModel(apiKey); } @@ -240,7 +240,28 @@ public class SaApiKeyDataLoaderImpl implements SaApiKeyDataLoader { - +### 6、多账号模式使用 + +如果系统有多套账号表,比如 Admin 和 User,只需要指定不同的命名空间即可: + +例如 User 账号的 API Key,我们使用原生 `SaApiKeyUtil` 进行创建与校验。 + +对于 Admin 账号的 API Key,我们则新建一个 `SaApiKeyTemplate` 实例 + +``` java +// 新建 Admin 账号的 apiKeyTemplate 对象,命名空间为 "admin-apikey" +public static SaApiKeyTemplate adminApiKeyTemplate = new SaApiKeyTemplate("admin-apikey"); + +// 创建一个新的 ApiKey,并返回 +@RequestMapping("/createApiKey") +public SaResult createApiKey() { + ApiKeyModel akModel = adminApiKeyTemplate.createApiKeyModel(StpUtil.getLoginId()).setTitle("test"); + adminApiKeyTemplate.saveApiKey(akModel); + return SaResult.data(akModel); +} + +// ...校验、查询等操作,均使用新创建的 adminApiKeyTemplate,而非原生 `SaApiKeyUtil` +```