Http参数验签(工具类)

前言

Http接口传输有很多加密方式, 如 AES 对称加密, RSA 非对称加密, 参数验签等, 下文主要简单示例参数如何验签。

原理

GET 参数和 appkey 通过如 MD5 之类的算法生成一个唯一不可逆的参数 sign, 参数传递的同时传递签名参数 sign。HTTP接收参数后根据自己持有的 appkeyappsecret 再次计算, 比对是否和传递的 sign 相同。

注意:appKey并不在http中传输,是计算摘要的密码盐

简单实现

maven依赖

1
2
3
4
5
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.12</version>
</dependency>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
**
* @author 当我遇上你
* @公众号 当我遇上你
* @since 2020-05-27
*
* 参数验签
*
* 现没有统一的签名机制,无需按业务区分。采用唯一的appKey和appSecret
*/
public class SignUtils {

private static final Logger logger = LoggerFactory.getLogger(SignUtils.class);

public static final String appKey = "idea360";
public static final String appSecret = "123456";

/**
* 获取签名参数
* @param params
* @return
*/
public static String getSignData(Map<String, String> params) {
StringBuilder content = new StringBuilder();
// key 自然排序
List<String> keys = new ArrayList<>(params.keySet());
Collections.sort(keys);

for (int i = 0; i < keys.size(); i++) {
String key = keys.get(i);
if ("sign".equals(key)) {
continue;
}
String value = params.get(key);
if (value != null) {
content.append(i == 0 ? "" : "&").append(key).append("=").append(value);
} else {
content.append(i == 0 ? "" : "&").append(key).append("=");
}
}

return content.toString();
}

/**
* 参数签名
* @param params
*/
public static String sign(Map<String, String> params) {

String outSignData = getSignData(params);
logger.info("outSignData:{}", outSignData);

byte[] hmac = new HmacUtils(HmacAlgorithms.HMAC_SHA_1, appSecret).hmac(outSignData);
String sign = new String(Base64.encodeBase64(hmac));
logger.info("sign:{}", sign);
return sign;
}

/**
* 参数验签
* @param params
*/
public static boolean checkSignature(Map<String, String> params) {
// 校验请求是否过期
String inTimeStamp = params.getOrDefault("_t", "0");
LocalDateTime inTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(Long.parseLong(inTimeStamp)), ZoneOffset.ofHours(8));
Duration duration = Duration.between(inTime, LocalDateTime.now());
long seconds = duration.get(SECONDS);
System.out.println("seconds: " + seconds);
if (seconds > 3 * 60) {
System.out.println("请求超时");
return Boolean.FALSE;
}

String inSignData = getSignData(params);
logger.info("inSignData:{}", inSignData);

byte[] inHmac = new HmacUtils(HmacAlgorithms.HMAC_SHA_1, appSecret).hmac(inSignData);
String sign2 = new String(Base64.encodeBase64(inHmac));
logger.info("sign2:{}", sign2);

String sign1 = params.getOrDefault("sign", "");
return sign1.equals(sign2);
}

/**
19:42:31.297 [main] INFO cn.idea360.demo.modules.login.SignUtils - outSignData:_t=1592048551294&appKey=idea360&c=v1&sessionId=123&state=&u=v2
19:42:31.385 [main] INFO cn.idea360.demo.modules.login.SignUtils - sign:EKL+wkPtAWBo/NIhR0Vb72khyKc=
seconds: 0
19:42:31.463 [main] INFO cn.idea360.demo.modules.login.SignUtils - inSignData:_t=1592048551294&appKey=idea360&c=v1&sessionId=123&state=&u=v2
19:42:31.463 [main] INFO cn.idea360.demo.modules.login.SignUtils - sign2:EKL+wkPtAWBo/NIhR0Vb72khyKc=
true
* @param args
*/
public static void main(String[] args) {
Map<String, String> params = new HashMap<>();
// TODO 传入为各个服务分配的的appKey
params.put("appKey", appKey);

// 业务参数
params.put("c", "v1");
params.put("u", "v2");
params.put("_t", String.valueOf(System.currentTimeMillis()));
params.put("state", null);
params.put("sessionId", "123");


// 签名
String sign1 = sign(params);
params.put("sign", sign1);

// 验签
boolean checkSignature = checkSignature(params);
System.out.println(checkSignature);

}

}

最后

本文到此结束,感谢阅读。如果您觉得不错,请关注公众号【当我遇上你】,您的支持是我写作的最大动力。