添加阿里云内网获取统一token类

This commit is contained in:
sh
2025-11-24 18:01:57 +08:00
parent cdf93bdfca
commit f37508674b

View File

@@ -0,0 +1,138 @@
package com.ruoyi.cms.handler;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
public class AliyunNlsTokenUtil {
private static final Logger logger = LoggerFactory.getLogger(AliyunNlsTokenUtil.class);
// Nginx 代理配置(与 Nginx 一致)
private static final String PROXY_HOST = "192.168.2.102";
private static final int PROXY_PORT = 10044;
// Token 接口代理地址(通过 Nginx 转发)
private static final String TOKEN_PROXY_URL = "http://" + PROXY_HOST + ":" + PROXY_PORT + "/";
// 单例 OkHttp 客户端(带代理,全局复用)
private static OkHttpClient proxyOkHttpClient;
static {
// 初始化带代理的 OkHttp 客户端(仅初始化一次)
Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(PROXY_HOST, PROXY_PORT));
proxyOkHttpClient = new OkHttpClient.Builder()
.proxy(proxy)
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.writeTimeout(60, TimeUnit.SECONDS)
.retryOnConnectionFailure(true)
.build();
}
/**
* 手动生成 Token绕开 SDK 无代理逻辑)
* @param accessKeyId 阿里云 AccessKeyId
* @param accessKeySecret 阿里云 AccessKeySecret
* @param accessToken SDK 的 AccessToken 对象(用于设置 token 和 expireTime
*/
public static void generateToken(String accessKeyId, String accessKeySecret, com.alibaba.nls.client.AccessToken accessToken) throws Exception {
// 1. 生成阿里云 API 签名HMAC-SHA1
String timestamp = getIso8601UtcTimestamp();
String nonce = String.valueOf(System.currentTimeMillis());
String signature = generateSignature(accessKeyId, accessKeySecret, timestamp, nonce);
// 2. 构造请求 URL带签名参数
String requestUrl = TOKEN_PROXY_URL +
"?Action=CreateToken" +
"&Format=JSON" +
"&Version=2019-02-28" +
"&AccessKeyId=" + URLEncoder.encode(accessKeyId, "UTF-8") +
"&Timestamp=" + URLEncoder.encode(timestamp, "UTF-8") +
"&SignatureNonce=" + URLEncoder.encode(nonce, "UTF-8") +
"&Signature=" + URLEncoder.encode(signature, "UTF-8") +
"&SignatureMethod=HMAC-SHA1" +
"&SignatureVersion=1.0";
// 打印完整请求 URL用于排查参数问题
logger.info("Token 生成请求 URL{}", requestUrl);
// 3. 发送 GET 请求(走 Nginx 代理)
Request request = new Request.Builder().url(requestUrl).build();
try (Response response = proxyOkHttpClient.newCall(request).execute()) {
String responseBody = response.body() != null ? response.body().string() : "无响应内容";
logger.info("Token 接口响应 - 状态码:{},响应体:{}", response.code(), responseBody);
if (!response.isSuccessful()) {
throw new RuntimeException("Token 生成失败,状态码:" + response.code() + ",错误信息:" + responseBody);
}
com.alibaba.fastjson.JSONObject json = com.alibaba.fastjson.JSON.parseObject(responseBody);
// 4. 提取 Token 和过期时间
String token = json.getJSONObject("Token").getString("Id");
long expireTime = json.getJSONObject("Token").getLong("ExpireTime");
// 5. 反射设置到 SDK 的 AccessToken 对象
setAccessTokenField(accessToken, "token", token);
setAccessTokenField(accessToken, "expireTime", expireTime);
logger.info("Token 生成成功,过期时间:{}", expireTime);
}
}
/**
* 生成阿里云 API 签名(遵循阿里云规范)
*/
private static String generateSignature(String accessKeyId, String accessKeySecret, String timestamp, String nonce) throws Exception {
// 1. 按字典序拼接参数(必须严格排序)
StringBuilder paramBuilder = new StringBuilder();
paramBuilder.append("AccessKeyId=").append(URLEncoder.encode(accessKeyId, "UTF-8"))
.append("&Action=").append(URLEncoder.encode("CreateToken", "UTF-8"))
.append("&Format=").append(URLEncoder.encode("JSON", "UTF-8"))
.append("&SignatureMethod=").append(URLEncoder.encode("HMAC-SHA1", "UTF-8"))
.append("&SignatureNonce=").append(URLEncoder.encode(nonce, "UTF-8"))
.append("&SignatureVersion=").append(URLEncoder.encode("1.0", "UTF-8"))
.append("&Timestamp=").append(URLEncoder.encode(timestamp, "UTF-8"))
.append("&Version=").append(URLEncoder.encode("2019-02-28", "UTF-8"));
// 2. 构造签名原文Method + & + 编码后的URL + & + 编码后的参数)
String method = "GET";
String encodedUrl = URLEncoder.encode("/", "UTF-8"); // 根路径编码
String encodedParams = URLEncoder.encode(paramBuilder.toString(), "UTF-8");
String signatureText = method + "&" + encodedUrl + "&" + encodedParams;
// 调试日志:对比服务器端预期的签名原文
logger.info("本地计算的签名原文:{}", signatureText);
// 3. HMAC-SHA1 加密 + Base64 编码
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(new SecretKeySpec((accessKeySecret + "&").getBytes("UTF-8"), "HmacSHA1"));
byte[] signatureBytes = mac.doFinal(signatureText.getBytes("UTF-8"));
return Base64.getEncoder().encodeToString(signatureBytes);
}
/**
* 生成 ISO8601 格式的 UTC 时间戳2025-11-24T10:00:00Z
*/
private static String getIso8601UtcTimestamp() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
sdf.setTimeZone(TimeZone.getTimeZone("UTC")); // 强制使用 UTC 时区
return sdf.format(new Date());
}
/**
* 反射设置 AccessToken 的私有字段
*/
private static void setAccessTokenField(com.alibaba.nls.client.AccessToken accessToken, String fieldName, Object value) throws Exception {
java.lang.reflect.Field field = com.alibaba.nls.client.AccessToken.class.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(accessToken, value);
}
}