背景
satoken签名对多应用支持不是很友好, 我在这里记录一下我改造的过程
代码结构
- config
- CustomerSaSignConfig
- handle
- CustomerSaCheckSignHandler
- template
- CustomerSaSignMany
- CustomerSaSignTemplate
具体代码
- CustomerSaSignConfig
import cn.dev33.satoken.exception.SaTokenException;
import cn.dev33.satoken.secure.SaSecureUtil;
import cn.dev33.satoken.sign.SaSignManager;
import cn.dev33.satoken.sign.annotation.SaCheckSign;
import cn.dev33.satoken.sign.config.SaSignConfig;
import cn.dev33.satoken.strategy.SaAnnotationStrategy;
import cn.hutool.core.collection.CollUtil;
import com.xm.common.saSign.handle.CySaCheckSignHandler;
import com.xm.common.saSign.template.CySaSignTemplate;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.cloud.context.scope.refresh.RefreshScopeRefreshedEvent;
import org.springframework.context.annotation.*;
import org.springframework.context.event.EventListener;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Map;
@Slf4j
@Configuration
public class CustomerSaSignConfigimplements SmartInitializingSingleton {
// 1. 启动完成后执行
@Override
public void afterSingletonsInstantiated() {
initCustomSignConfig();
}
// 2. 监听 Nacos 配置刷新事件 (关键)
@EventListener(RefreshScopeRefreshedEvent.class)
public void onRefresh(RefreshScopeRefreshedEvent event) {
log.info("检测到配置刷新,重新注册自定义签名配置...");
initCustomSignConfig();
}
private void initCustomSignConfig() {
// 移除 & 注册 Handler
SaAnnotationStrategy.instance.removeAnnotationHandler(SaCheckSign.class);
SaAnnotationStrategy.instance.registerAnnotationHandler(new CySaCheckSignHandler());
// 重新设置 Template
SaSignManager.setSaSignTemplate(new CySaSignTemplate());
// 核心:重新绑定 Digest 算法
SaSignConfig config = SaSignManager.getConfig();
config.setDigestMethod((fullStr) -> customDigest(fullStr, config));
// 处理 signMany (多 AppId 配置)
Map<String, SaSignConfig> signMany = SaSignManager.getSignMany();
if (CollUtil.isNotEmpty(signMany)) {
signMany.forEach((appId, subConfig) -> {
log.info("重新绑定 appId: {} 的签名函数", appId);
subConfig.setDigestMethod((fullStr) -> customDigest(fullStr, subConfig));
});
}
}
/**
* 抽取的自定义摘要算法方法
* @param fullStr 待摘要的字符串
* @param config SaSign 配置对象
* @return 摘要后的结果
*/
private static String customDigest(String fullStr, SaSignConfig config) {
String digestAlgo = config.getDigestAlgo();
String secretKey = config.getSecretKey();
// 1. 处理需要密钥的 HMAC 算法
if ("hmacSha256".equalsIgnoreCase(digestAlgo)) {
try {
Mac mac = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(
secretKey.getBytes(StandardCharsets.UTF_8), "HmacSHA256"
);
mac.init(secret_key);
byte[] binaryData = mac.doFinal(fullStr.getBytes(StandardCharsets.UTF_8));
return Base64.encodeBase64String(binaryData);
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
throw new SaTokenException("HMAC 签名异常", e);
}
}
return switch (digestAlgo.toLowerCase()) {
case "md5" -> SaSecureUtil.md5(fullStr);
case "sha1" -> SaSecureUtil.sha1(fullStr);
case "sha256" -> SaSecureUtil.sha256(fullStr);
case "sha384" -> SaSecureUtil.sha384(fullStr);
case "sha512" -> SaSecureUtil.sha512(fullStr);
default -> throw new SaTokenException("不支持的摘要算法:" + digestAlgo);
};
}
}
- CustomerSaCheckSignHandler
import cn.dev33.satoken.annotation.handler.SaAnnotationHandlerInterface;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.context.model.SaRequest;
import cn.dev33.satoken.sign.annotation.SaCheckSign;
import com.xm.common.saSign.template.CySaSignMany;
import java.lang.reflect.AnnotatedElement;
public class CySaCheckSignHandler implements SaAnnotationHandlerInterface<SaCheckSign> {
@Override
public Class<SaCheckSign> getHandlerAnnotationClass() {
return SaCheckSign.class;
}
@Override
public void checkMethod(SaCheckSign at, AnnotatedElement element) {
_checkMethod(at.appid(), at.verifyParams());
}
public static void _checkMethod(String appid, String[] verifyParams) {
SaRequest req = SaHolder.getRequest();
// 如果 appid 为 #{} 格式,则从请求参数中获取
if(appid.startsWith("#{") && appid.endsWith("}")) {
String reqParamName = appid.substring(2, appid.length() - 1);
appid = req.getParam(reqParamName);
}
CySaSignMany.getSignTemplate(appid).checkRequest(req, verifyParams);
}
}
- CustomerSaSignMany
import cn.dev33.satoken.fun.SaParamRetFunction;
import cn.dev33.satoken.sign.SaSignManager;
import cn.dev33.satoken.sign.config.SaSignConfig;
import cn.dev33.satoken.sign.error.SaSignErrorCode;
import cn.dev33.satoken.sign.exception.SaSignException;
import cn.dev33.satoken.sign.template.SaSignTemplate;
import cn.dev33.satoken.util.SaFoxUtil;
public class CySaSignMany {
/**
* 根据 appid 获取 SaSignConfig,允许自定义
*/
public static SaParamRetFunction<String, SaSignConfig> findSaSignConfigMethod = (appid) -> SaSignManager.getSignMany().get(appid);
/**
* 获取 SaSignTemplate,根据 appid
* @param appid /
* @return /
*/
public static SaSignTemplate getSignTemplate(String appid) {
// appid 为空,返回全局默认 SaSignTemplate
if(SaFoxUtil.isEmpty(appid)){
return SaSignManager.getSaSignTemplate();
}
// 获取 SaSignConfig
SaSignConfig config = findSaSignConfigMethod.run(appid);
if(config == null){
throw new SaSignException("未找到签名配置,appid=" + appid).setCode(SaSignErrorCode.CODE_12211);
}
// 创建 SaSignTemplate 并返回
return new CySaSignTemplate(config);
}
}
- CustomerSaSignTemplate
import cn.dev33.satoken.context.model.SaRequest;
import cn.dev33.satoken.sign.config.SaSignConfig;
import cn.dev33.satoken.sign.template.SaSignTemplate;
import cn.hutool.core.util.StrUtil;
import java.util.Map;
import java.util.TreeMap;
public class CySaSignTemplate extends SaSignTemplate {
public CySaSignTemplate() {
super();
}
public CySaSignTemplate(SaSignConfig signConfig) {
super(signConfig);
}
@Override
public void checkRequest(SaRequest request, String... paramNames) {
if (paramNames.length == 0) {
checkParamMap(takeRequestParam(request, new String[]{}));
} else {
checkParamMap(takeRequestParam(request, paramNames));
}
}
@Override
protected Map<String, String> takeRequestParam(SaRequest request, String [] paramNames) {
Map<String, String> paramMap = new TreeMap<>();
// 此三个参数是必须获取的
paramMap.put(timestamp, getParam(request, timestamp));
paramMap.put(nonce, getParam(request, nonce));
paramMap.put(sign, getParam(request, sign));
// 获取指定的参数
for (String paramName : paramNames) {
paramMap.put(paramName, request.getParam(paramName));
}
// 返回
return paramMap;
}
private String getParam(SaRequest request, String paramName){
if (StrUtil.isBlank(paramName)){
return null;
}
// 优先从请求头中获取
String header = request.getHeader(paramName);
if (StrUtil.isNotBlank(header)){
return header;
}
// 从请求参数中获取
return request.getParam(paramName);
}
}
插件支持多应用改造&spm=1001.2101.3001.5002&articleId=158464978&d=1&t=3&u=c8912b0cfd6f45bfaf7c0c20a08352ab)
367

被折叠的 条评论
为什么被折叠?



