feat: API Key 模块新增 namespace 命名空间,为多账号模式提供支持

This commit is contained in:
click33 2025-04-10 20:38:47 +08:00
parent 49ec4d9690
commit fab7f200cc
6 changed files with 66 additions and 27 deletions

View File

@ -41,20 +41,41 @@ import java.util.List;
*/ */
public class SaApiKeyTemplate { public class SaApiKeyTemplate {
/**
*默认命名空间
*/
public static final String DEFAULT_NAMESPACE = "apikey";
/**
* 命名空间
*/
public String namespace;
/** /**
* Raw Session 读写委托 * Raw Session 读写委托
*/ */
public SaRawSessionDelegator rawSessionDelegator = new SaRawSessionDelegator("apikey"); public SaRawSessionDelegator rawSessionDelegator;
/** /**
* raw-session 中的保存索引列表使用的 key * raw-session 中的保存索引列表使用的 key
*/ */
public static final String API_KEY_LIST = "__HD_API_KEY_LIST"; 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 // ------------------- ApiKey
@ -73,7 +94,7 @@ public class SaApiKeyTemplate {
* @return / * @return /
*/ */
public ApiKeyModel getApiKeyModelFromDatabase(String apiKey) { 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()); SaSession session = rawSessionDelegator.getSessionById(ak.getLoginId());
ArrayList<String> apiKeyList = session.get(API_KEY_LIST, ArrayList::new); ArrayList<String> apiKeyList = session.get(API_KEY_LIST, ArrayList::new);
@ -170,7 +191,7 @@ public class SaApiKeyTemplate {
getSaTokenDao().deleteObject(splicingApiKeySaveKey(apiKey)); getSaTokenDao().deleteObject(splicingApiKeySaveKey(apiKey));
// 删索引 // 删索引
if(SaManager.getSaApiKeyDataLoader().getIsRecordIndex()) { if(getIsRecordIndex()) {
// RawSession 中不存在提前退出 // RawSession 中不存在提前退出
SaSession session = rawSessionDelegator.getSessionById(ak.getLoginId(), false); SaSession session = rawSessionDelegator.getSessionById(ak.getLoginId(), false);
if(session == null) { if(session == null) {
@ -199,7 +220,7 @@ public class SaApiKeyTemplate {
*/ */
public void deleteApiKeyByLoginId(Object loginId) { public void deleteApiKeyByLoginId(Object loginId) {
// 先判断是否开启索引 // 先判断是否开启索引
if(! SaManager.getSaApiKeyDataLoader().getIsRecordIndex()) { if(! getIsRecordIndex()) {
SaManager.getLog().warn("当前 API Key 模块未开启索引记录功能,无法执行 deleteApiKeyByLoginId 操作"); SaManager.getLog().warn("当前 API Key 模块未开启索引记录功能,无法执行 deleteApiKeyByLoginId 操作");
return; return;
} }
@ -375,7 +396,7 @@ public class SaApiKeyTemplate {
*/ */
public void adjustIndex(Object loginId, SaSession session) { public void adjustIndex(Object loginId, SaSession session) {
// 先判断是否开启索引 // 先判断是否开启索引
if(! SaManager.getSaApiKeyDataLoader().getIsRecordIndex()) { if(! getIsRecordIndex()) {
SaManager.getLog().warn("当前 API Key 模块未开启索引记录功能,无法执行 adjustIndex 操作"); SaManager.getLog().warn("当前 API Key 模块未开启索引记录功能,无法执行 adjustIndex 操作");
return; return;
} }
@ -431,7 +452,7 @@ public class SaApiKeyTemplate {
*/ */
public List<ApiKeyModel> getApiKeyList(Object loginId) { public List<ApiKeyModel> getApiKeyList(Object loginId) {
// 先判断是否开启索引 // 先判断是否开启索引
if(! SaManager.getSaApiKeyDataLoader().getIsRecordIndex()) { if(! getIsRecordIndex()) {
SaManager.getLog().warn("当前 API Key 模块未开启索引记录功能,无法执行 getApiKeyList 操作"); SaManager.getLog().warn("当前 API Key 模块未开启索引记录功能,无法执行 getApiKeyList 操作");
return new ArrayList<>(); return new ArrayList<>();
} }
@ -464,13 +485,13 @@ public class SaApiKeyTemplate {
public String readApiKeyValue(SaRequest request) { public String readApiKeyValue(SaRequest request) {
// 优先从请求参数中获取 // 优先从请求参数中获取
String apiKey = request.getParam(API_KEY_PARAMETER_NAME); String apiKey = request.getParam(namespace);
if(SaFoxUtil.isNotEmpty(apiKey)) { if(SaFoxUtil.isNotEmpty(apiKey)) {
return apiKey; return apiKey;
} }
// 然后请求头 // 然后请求头
apiKey = request.getHeader(API_KEY_PARAMETER_NAME); apiKey = request.getHeader(namespace);
if(SaFoxUtil.isNotEmpty(apiKey)) { if(SaFoxUtil.isNotEmpty(apiKey)) {
return apiKey; return apiKey;
} }
@ -496,7 +517,6 @@ public class SaApiKeyTemplate {
} }
// ------------------- 拼接key // ------------------- 拼接key
/** /**
@ -505,7 +525,7 @@ public class SaApiKeyTemplate {
* @return key * @return key
*/ */
public String splicingApiKeySaveKey(String apiKey) { 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() { public boolean getIsRecordIndex() {
// if(! SaManager.getSaApiKeyDataLoader().getIsRecordIndex()) { return SaManager.getSaApiKeyDataLoader().getIsRecordIndex();
// SaManager.getLog().warn("当前 API Key 模块未开启索引记录功能,无法执行此操作"); }
// throw new ApiKeyException("当前 API Key 模块未开启索引记录功能,无法执行此操作").setCode(SaErrorCode.CODE_12305);
// }
// }
} }

View File

@ -38,10 +38,11 @@ public interface SaApiKeyDataLoader {
/** /**
* 根据 apiKey 从数据库获取 ApiKeyModel 信息 实现此方法无需为数据做缓存处理框架内部已包含缓存逻辑 * 根据 apiKey 从数据库获取 ApiKeyModel 信息 实现此方法无需为数据做缓存处理框架内部已包含缓存逻辑
* *
* @param namespace /
* @param apiKey / * @param apiKey /
* @return ApiKeyModel * @return ApiKeyModel
*/ */
default ApiKeyModel getApiKeyModelFromDatabase(String apiKey) { default ApiKeyModel getApiKeyModelFromDatabase(String namespace, String apiKey) {
return null; return null;
} }

View File

@ -24,7 +24,7 @@ public class SaApiKeyDataLoaderImpl implements SaApiKeyDataLoader {
// 根据 apiKey 从数据库获取 ApiKeyModel 信息 实现此方法无需为数据做缓存处理框架内部已包含缓存逻辑 // 根据 apiKey 从数据库获取 ApiKeyModel 信息 实现此方法无需为数据做缓存处理框架内部已包含缓存逻辑
@Override @Override
public ApiKeyModel getApiKeyModelFromDatabase(String apiKey) { public ApiKeyModel getApiKeyModelFromDatabase(String namespace, String apiKey) {
return apiKeyMockMapper.getApiKeyModel(apiKey); return apiKeyMockMapper.getApiKeyModel(apiKey);
} }

View File

@ -1,7 +1,7 @@
package com.pj.mock; package com.pj.mock;
import cn.dev33.satoken.apikey.model.ApiKeyModel; import cn.dev33.satoken.apikey.model.ApiKeyModel;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Component;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -12,7 +12,7 @@ import java.util.Map;
* @author click33 * @author click33
* @since 2025/4/4 * @since 2025/4/4
*/ */
@Service @Component
public class SaApiKeyMockMapper { public class SaApiKeyMockMapper {
// 添加模拟测试数据 // 添加模拟测试数据

View File

@ -11,5 +11,5 @@ public class SaTokenJwtDemoApplication {
SpringApplication.run(SaTokenJwtDemoApplication.class, args); SpringApplication.run(SaTokenJwtDemoApplication.class, args);
System.out.println("\n启动成功Sa-Token配置如下" + SaManager.getConfig()); System.out.println("\n启动成功Sa-Token配置如下" + SaManager.getConfig());
} }
} }

View File

@ -223,7 +223,7 @@ public class SaApiKeyDataLoaderImpl implements SaApiKeyDataLoader {
// 根据 apiKey 从数据库获取 ApiKeyModel 信息 (实现此方法无需为数据做缓存处理,框架内部已包含缓存逻辑) // 根据 apiKey 从数据库获取 ApiKeyModel 信息 (实现此方法无需为数据做缓存处理,框架内部已包含缓存逻辑)
@Override @Override
public ApiKeyModel getApiKeyModelFromDatabase(String apiKey) { public ApiKeyModel getApiKeyModelFromDatabase(String namespace, String apiKey) {
return apiKeyMapper.getApiKeyModel(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`
```