修改
This commit is contained in:
@@ -1,9 +1,11 @@
|
|||||||
package com.yxt.ss.gateway.api.rest;
|
package com.yxt.ss.gateway.api.rest;
|
||||||
|
|
||||||
import com.yxt.ss.gateway.api.utils.ResultBean;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.yxt.ss.gateway.api.utils.SignatureQuery;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.yxt.ss.gateway.api.utils.SignatureUtil;
|
import com.yxt.ss.gateway.api.utils.*;
|
||||||
import okhttp3.*;
|
import okhttp3.*;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
@@ -15,6 +17,7 @@ import java.io.UnsupportedEncodingException;
|
|||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.time.Instant;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
@@ -29,26 +32,42 @@ import java.util.concurrent.TimeUnit;
|
|||||||
@RequestMapping("/signature")
|
@RequestMapping("/signature")
|
||||||
public class Signature {
|
public class Signature {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AppKeyConfig appKeyConfig;
|
||||||
|
|
||||||
|
public String getSecret(String appKey) {
|
||||||
|
return appKeyConfig.getKeys().get(appKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//appkey
|
//appkey
|
||||||
static final String APPKEY = "appkey";
|
static final String APPKEY = "appKey4";
|
||||||
//secret
|
//secret
|
||||||
static final String SECRET = "secret";
|
static final String SECRET = "secret";
|
||||||
|
|
||||||
//获取签名
|
//获取参数
|
||||||
@PostMapping("/getSign")
|
@PostMapping("/getSign")
|
||||||
ResultBean<String> getSign(SignatureQuery query) {
|
ResultBean getSign(SignatureQuery query) {
|
||||||
ResultBean<String> rb = ResultBean.fireFail();
|
ResultBean<String> rb = ResultBean.fireFail();
|
||||||
try {
|
try {
|
||||||
Map<String, String> formData = new HashMap<>();
|
Map<String, String> formData = query.getParameters();
|
||||||
// Map<String, String> map = query.getParameters();
|
Map<String, String> tree = new TreeMap<>(formData);
|
||||||
// formData.put("key1", map.getOrDefault("key1", "").toString());
|
tree.put("_app", APPKEY);
|
||||||
formData = query.getParameters();
|
tree.put("_t", String.valueOf(System.currentTimeMillis() / 1000));
|
||||||
String appKey = query.getAppKey();
|
tree.put("_s", "");
|
||||||
String secret = query.getSecret();
|
|
||||||
|
|
||||||
// 生成签名
|
// 生成签名
|
||||||
String sign = SignatureUtil.generateSignature(formData, appKey, secret);
|
String sign = SignatureUtil.generateSignature(tree, SECRET);
|
||||||
return rb.success().setData(sign);
|
//添加签名值map
|
||||||
|
tree.put("_sign", sign);
|
||||||
|
//发起请求
|
||||||
|
ResultBean resultBean = client(tree);
|
||||||
|
if (!resultBean.getSuccess()) {
|
||||||
|
return rb.setMsg(resultBean.getMsg());
|
||||||
|
}
|
||||||
|
//通过验证继续调用接口
|
||||||
|
|
||||||
|
|
||||||
|
return rb.success();
|
||||||
} catch (UnsupportedEncodingException e) {
|
} catch (UnsupportedEncodingException e) {
|
||||||
return rb.setMsg("Unsupported encoding: " + e.getMessage());
|
return rb.setMsg("Unsupported encoding: " + e.getMessage());
|
||||||
} catch (NoSuchAlgorithmException e) {
|
} catch (NoSuchAlgorithmException e) {
|
||||||
@@ -56,7 +75,71 @@ public class Signature {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
//验证
|
||||||
|
@PostMapping("/validate")
|
||||||
|
ResultBean validate(Map<String, String> data) {
|
||||||
|
ResultBean rb = ResultBean.fireFail();
|
||||||
|
Map<String, String> parameters = data;
|
||||||
|
//1、解析参数,校验_app是否正确,_t是否在5分钟内。
|
||||||
|
if (!parameters.containsKey("_app") ||
|
||||||
|
parameters.get("_app") == null ||
|
||||||
|
parameters.get("_app").trim().isEmpty()) {
|
||||||
|
return rb.setMsg("_app参数缺失或无效");
|
||||||
|
}
|
||||||
|
//2、根据_app参数获取对应的secret值。
|
||||||
|
String secret = getSecret(parameters.get("_app"));
|
||||||
|
if (StringUtils.isEmpty(secret)) {
|
||||||
|
return rb.setMsg("_app参数不正确");
|
||||||
|
}
|
||||||
|
if (parameters.containsKey("_t") ||
|
||||||
|
parameters.get("_t") == null ||
|
||||||
|
parameters.get("_t").trim().isEmpty()) {
|
||||||
|
return rb.setMsg("_t参数缺失");
|
||||||
|
}
|
||||||
|
|
||||||
|
String _t = parameters.get("_t");
|
||||||
|
// 获取当前的秒级时间戳
|
||||||
|
long currentTimestamp = Instant.now().getEpochSecond();
|
||||||
|
// 将字符串转换为 long 类型
|
||||||
|
long timestamp = Long.parseLong(_t);
|
||||||
|
// 计算时间差,允许最大偏差 5 分钟(300秒)
|
||||||
|
long timeDifference = Math.abs(currentTimestamp - timestamp);
|
||||||
|
|
||||||
|
if (timeDifference > 300) {
|
||||||
|
return rb.setMsg("时间已超过5分钟,时间失效");
|
||||||
|
}
|
||||||
|
String _sign = parameters.get("_sign");
|
||||||
|
//3、检验签名,成功则继续调用接口,失败返回失败信息。
|
||||||
|
parameters.remove("_sign");
|
||||||
|
try {
|
||||||
|
// 3.1. 重新生成签名
|
||||||
|
String calculatedSignature = SignatureUtil.generateSignature(parameters, secret);
|
||||||
|
|
||||||
|
// 3.2. 使用固定时间比较方式验证签名
|
||||||
|
boolean valid = MessageDigest.isEqual(
|
||||||
|
calculatedSignature.getBytes("UTF-8"),
|
||||||
|
_sign.getBytes("UTF-8")
|
||||||
|
);
|
||||||
|
if (!valid) {
|
||||||
|
return rb.setMsg("签名不正确");
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
return rb.setMsg("Encoding error: " + e.getMessage());
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
return rb.setMsg("Algorithm error: " + e.getMessage());
|
||||||
|
} catch (Exception e) {
|
||||||
|
return rb.setMsg("Unexpected error: " + e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return rb.success();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResultBean client(Map<String, String> data) {
|
||||||
|
ResultBean rb = ResultBean.fireFail();
|
||||||
OkHttpClient client = new OkHttpClient.Builder()
|
OkHttpClient client = new OkHttpClient.Builder()
|
||||||
.connectTimeout(10, TimeUnit.SECONDS)
|
.connectTimeout(10, TimeUnit.SECONDS)
|
||||||
.writeTimeout(10, TimeUnit.SECONDS)
|
.writeTimeout(10, TimeUnit.SECONDS)
|
||||||
@@ -66,14 +149,7 @@ public class Signature {
|
|||||||
try {
|
try {
|
||||||
// 构建URL
|
// 构建URL
|
||||||
String endPoint = "http://127.0.0.1:9999";
|
String endPoint = "http://127.0.0.1:9999";
|
||||||
String path = "/signature/getSign";
|
String path = "/signature/validate";
|
||||||
|
|
||||||
// 假设data是一个包含所有参数的字典
|
|
||||||
Map<String, String> data = new HashMap<>();
|
|
||||||
data.put("parameters[key1]", "value1");
|
|
||||||
data.put("parameters[key2]", "value2");
|
|
||||||
data.put("appKey", "testAppKey");
|
|
||||||
data.put("secret", "testSecret");
|
|
||||||
|
|
||||||
// 创建FormData
|
// 创建FormData
|
||||||
FormBody.Builder formBuilder = new FormBody.Builder();
|
FormBody.Builder formBuilder = new FormBody.Builder();
|
||||||
@@ -94,93 +170,28 @@ public class Signature {
|
|||||||
|
|
||||||
// 发送请求
|
// 发送请求
|
||||||
try (Response response = client.newCall(request).execute()) {
|
try (Response response = client.newCall(request).execute()) {
|
||||||
|
String responseBody = response.body().string();
|
||||||
|
// 使用 Jackson 解析 JSON 响应
|
||||||
|
ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
JsonNode jsonNode = objectMapper.readTree(responseBody);
|
||||||
|
String success = jsonNode.path("success").asText();
|
||||||
|
String msg = jsonNode.path("msg").asText();
|
||||||
|
if ("false".equals(success)) {
|
||||||
|
return rb.setMsg(msg);
|
||||||
|
}
|
||||||
if (response.isSuccessful()) {
|
if (response.isSuccessful()) {
|
||||||
System.out.println("Response: " + response.body().string());
|
System.out.println("Response: " + response.body().string());
|
||||||
} else {
|
} else {
|
||||||
System.err.println("Request failed: " + response.code());
|
System.err.println("Request failed: " + response.message());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
System.err.println("Network error: " + e.getMessage());
|
System.err.println("Network error: " + e.getMessage());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
System.err.println("Unexpected error: " + e.getMessage());
|
System.err.println("Unexpected error: " + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
return rb.success();
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 对传入的参数集合进行签名处理
|
|
||||||
* 返回系统参数
|
|
||||||
*/
|
|
||||||
@PostMapping("/getSignParameters")
|
|
||||||
public ResultBean<Map<String, String>> signParameters(SignatureQuery query) {
|
|
||||||
ResultBean<Map<String, String>> rb = ResultBean.fireFail();
|
|
||||||
|
|
||||||
try {
|
|
||||||
Map<String, String> parameters = query.getParameters();
|
|
||||||
|
|
||||||
// 2. 使用 TreeMap 按照字典顺序对参数进行排序
|
|
||||||
Map<String, String> tree = new TreeMap<>(parameters);
|
|
||||||
tree.put("_app", query.getAppKey());
|
|
||||||
tree.putIfAbsent("_t", String.valueOf(System.currentTimeMillis()));
|
|
||||||
|
|
||||||
// 3. 拼接参数串
|
|
||||||
String content = SignatureUtil.joinParameters(tree);
|
|
||||||
|
|
||||||
// 4. 将参数串前后拼接密钥
|
|
||||||
content = query.getSecret() + content + query.getSecret();
|
|
||||||
|
|
||||||
// 5. 计算 sign 值
|
|
||||||
String sign = SignatureUtil.md5(content);
|
|
||||||
|
|
||||||
// 6. 添加系统参数
|
|
||||||
tree.put("_sign", sign);
|
|
||||||
tree.put("_s", "");
|
|
||||||
|
|
||||||
return rb.success().setData(tree);
|
|
||||||
|
|
||||||
} catch (UnsupportedEncodingException e) {
|
|
||||||
return rb.setMsg("Encoding error: " + e.getMessage());
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
return rb.setMsg("Algorithm error: " + e.getMessage());
|
|
||||||
} catch (Exception e) {
|
|
||||||
return rb.setMsg("Unexpected error: " + e.getMessage());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 验证签名是否正确
|
|
||||||
*
|
|
||||||
* @return 是否验证通过
|
|
||||||
*/
|
|
||||||
@PostMapping("/validateSignature")
|
|
||||||
public ResultBean<Boolean> validateSignature(SignatureQuery query) {
|
|
||||||
ResultBean<Boolean> rb = ResultBean.fireFail();
|
|
||||||
|
|
||||||
try {
|
|
||||||
Map<String, String> parameters = query.getParameters();
|
|
||||||
String appKey = query.getAppKey();
|
|
||||||
String secret = query.getSecret();
|
|
||||||
|
|
||||||
|
|
||||||
// 2. 重新生成签名
|
|
||||||
String calculatedSignature = SignatureUtil.generateSignature(parameters, appKey, secret);
|
|
||||||
|
|
||||||
// 3. 使用固定时间比较方式验证签名
|
|
||||||
boolean valid = MessageDigest.isEqual(
|
|
||||||
calculatedSignature.getBytes("UTF-8"),
|
|
||||||
query.getReceivedSignature().getBytes("UTF-8")
|
|
||||||
);
|
|
||||||
|
|
||||||
return rb.success().setData(valid);
|
|
||||||
} catch (UnsupportedEncodingException e) {
|
|
||||||
return rb.setMsg("Encoding error: " + e.getMessage());
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
return rb.setMsg("Algorithm error: " + e.getMessage());
|
|
||||||
} catch (Exception e) {
|
|
||||||
return rb.setMsg("Unexpected error: " + e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
26
src/main/java/com/yxt/ss/gateway/api/utils/AppKeyConfig.java
Normal file
26
src/main/java/com/yxt/ss/gateway/api/utils/AppKeyConfig.java
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
package com.yxt.ss.gateway.api.utils;
|
||||||
|
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description:
|
||||||
|
* @author: dimengzhe
|
||||||
|
* @date: 2024/12/9
|
||||||
|
**/
|
||||||
|
@Component
|
||||||
|
@ConfigurationProperties(prefix = "app.keys")
|
||||||
|
public class AppKeyConfig {
|
||||||
|
|
||||||
|
private Map<String, String> keys;
|
||||||
|
|
||||||
|
public Map<String, String> getKeys() {
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKeys(Map<String, String> keys) {
|
||||||
|
this.keys = keys;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,13 +13,7 @@ import java.util.Map;
|
|||||||
@Data
|
@Data
|
||||||
public class SignatureQuery {
|
public class SignatureQuery {
|
||||||
|
|
||||||
private String appKey;
|
//传入的参数
|
||||||
|
|
||||||
private String secret;
|
|
||||||
|
|
||||||
//业务参数
|
|
||||||
private Map<String, String> parameters = new HashMap<>();
|
private Map<String, String> parameters = new HashMap<>();
|
||||||
|
|
||||||
//原签名值
|
|
||||||
private String receivedSignature;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,18 +19,13 @@ public class SignatureUtil {
|
|||||||
* 生成请求签名
|
* 生成请求签名
|
||||||
*
|
*
|
||||||
* @param parameters 请求参数
|
* @param parameters 请求参数
|
||||||
* @param appKey 应用的 AppKey
|
|
||||||
* @param secret 密钥
|
* @param secret 密钥
|
||||||
* @return 签名
|
* @return 签名
|
||||||
*/
|
*/
|
||||||
public static String generateSignature(Map<String, String> parameters, String appKey, String secret) throws UnsupportedEncodingException, NoSuchAlgorithmException {
|
public static String generateSignature(Map<String, String> parameters, String secret) throws UnsupportedEncodingException, NoSuchAlgorithmException {
|
||||||
// 1. 使用 TreeMap 按照字典顺序对参数进行排序
|
|
||||||
Map<String, String> sortedParams = new TreeMap<>(parameters);
|
|
||||||
sortedParams.put("_app", appKey); // 添加应用的 AppKey
|
|
||||||
sortedParams.put("_t", String.valueOf(System.currentTimeMillis())); // 添加时间戳
|
|
||||||
|
|
||||||
// 2. 拼接参数字符串
|
// 2. 拼接参数字符串
|
||||||
String content = joinParameters(sortedParams);
|
String content = joinParameters(parameters);
|
||||||
|
|
||||||
// 3. 将密钥加在参数字符串的前后
|
// 3. 将密钥加在参数字符串的前后
|
||||||
content = secret + content + secret;
|
content = secret + content + secret;
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
app:
|
||||||
|
keys:
|
||||||
|
appKey1: secret1
|
||||||
|
appKey2: secret2
|
||||||
|
appKey3: secret3
|
||||||
@@ -6,3 +6,9 @@ spring:
|
|||||||
profiles:
|
profiles:
|
||||||
active: devv
|
active: devv
|
||||||
|
|
||||||
|
logging:
|
||||||
|
level:
|
||||||
|
org:
|
||||||
|
springframework:
|
||||||
|
context: DEBUG
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user