数字签名
概述
在微服务架构中,服务之间的安全通信是一项重要任务。为了验证发送方身份并确保数据在传递过程中不被篡改,我们希望使用加签和验签的机制来完成此项功能。
快速上手
1、在业务模块的pom
文件中添加依赖
html
<!--pangea公共工具包 -->
<dependency>
<groupId>com.hisense.pangea</groupId>
<artifactId>pangea-common-utils</artifactId>
<version>${pangea.version}</version>
</dependency>
2、生成签名工具类———SignUtils
java
String sign = SignUtils.generateSign(signParam, algorithm, type);
方法参数如下:
参数 | 类型 | 默认值 | 描述 |
---|---|---|---|
signParam | SignParam | 无 | 签名算法密钥 secretKey; 时间戳 timestamp; 请求体 body; 请求参数 param; 响应数据 data; 响应码 code; 响应信息 msg; 告警提示码; alert |
algorithm | String | md5 | 加密算法 "md5" 或 "sha-256" |
type | String | 无 | 签名类型 "request" 或 "response" |
3、约定请求头和响应头中携带的信息
java
timestamp // 时间戳
signature // 数字签名
systemName // 系统编码
4、约定配置信息(联系盘古团队)
java
systemName: L0099 // 系统编码
secretKey: ac17162bc09iu88 // 签名算法密钥
algorism: md5 // 签名算法
sign-urls: // 验签名单
- /system/org/
ignore-urls: // 白名单
- /system/org/getOrgInfoAndUserInfoByName
5、签名校验demo(JavaScript)
javaScript
const CryptoJS = require('crypto-js');
const SIGN_TYPE_REQUEST = 'request';
const SIGN_TYPE_RESPONSE = 'response';
const SIGN_PARAM_BODY = 'body';
const SIGN_PARAM_PARAM = 'param';
const SIGN_PARAM_SECRET_KEY = 'secretKey';
const SIGN_PARAM_TIMESTAMP = 'timestamp';
const SIGN_PARAM_DATA = 'data';
const SIGN_PARAM_CODE = 'code';
const SIGN_PARAM_MSG = 'msg';
const SIGN_PARAM_ALERT = 'alert';
const ALGORITHM_SHA_256 = 'SHA-256';
const ALGORITHM_MD5 = 'MD5';
// 签名算法
function generateSign(signParam, algorithm, type) {
const params = {};
if (type.toLowerCase() === SIGN_TYPE_REQUEST) {
params[SIGN_PARAM_BODY] = signParam.body;
params[SIGN_PARAM_PARAM] = signParam.param;
params[SIGN_PARAM_SECRET_KEY] = signParam.secretKey;
params[SIGN_PARAM_TIMESTAMP] = signParam.timestamp;
} else if (type.toLowerCase() === SIGN_TYPE_RESPONSE) {
params[SIGN_PARAM_TIMESTAMP] = signParam.timestamp;
params[SIGN_PARAM_DATA] = signParam.data;
params[SIGN_PARAM_CODE] = signParam.code;
params[SIGN_PARAM_MSG] = signParam.msg;
params[SIGN_PARAM_ALERT] = signParam.alert;
params[SIGN_PARAM_SECRET_KEY] = signParam.secretKey;
} else {
return `Invalid sign type: ${type}`;
}
const sortedParams = Object.keys(params).sort();
let signStr = '';
for (const key of sortedParams) {
if (key === SIGN_PARAM_PARAM && typeof params[key] === 'object') {
const map = params[key];
const sortedEntries = Object.keys(map).sort();
for (const entryKey of sortedEntries) {
signStr += `${entryKey}=${map[entryKey]}&`;
}
} else {
signStr += `${key}=${params[key]}&`;
}
}
// 去除最后一个 '&'
signStr = signStr.slice(0, -1);
// 去除空格 \t 跳格 \r 回车 \n 换行
const signAssemble = signStr.replaceAll(/\s+/g, '');
console.log(`signAssemble: ${signAssemble}`);
// 选择签名算法
if (algorithm.toUpperCase() === ALGORITHM_SHA_256) {
return getSha256Digest(signAssemble);
} else {
return getMd5Digest(signAssemble);
}
}
function getSha256Digest(input) {
return CryptoJS.SHA256(input).toString(CryptoJS.enc.Hex);
}
function getMd5Digest(input) {
return CryptoJS.MD5(input).toString(CryptoJS.enc.Hex);
}
// 响应签名 入参示例
const signParamResponse = {
timestamp: '1749775114496',
secretKey: 'bd17162bc09iu88',
code: '0',
msg: '操作成功',
data: '{"userInfoList":[{"loginName":"wangwu","ldapFullPath":"海信集团\\\\电子信息集团","userName":"王五","userId":504885}],"orgInfoList":[]}',
alert: '0'
};
// 请求签名 入参示例
const signParamRequest = {
timestamp: "1750227296839",
secretKey: "bd17162bc09iu88",
body: "{\r\n" +
" \"name\":\"zhangsan\",\n" +
" \"typeCode\":\"LDAP\"\r\n" +
"}",
param: {
name: "lisi",
typeCode: "LDAP"
}
};
// 发起请求
const timestamp = Date.now().toString(); // 获取当前时间戳
const signature = generateSign(signParamRequest, 'SHA-256', 'Request'); // 生成请求签名
const response = await axios.get('/api/endpoint', {
params: { param },
headers: {
timestamp,
signature,
systemName
}
})
// 处理响应
const { data, headers } = response;
const responseTimestamp = headers['timestamp'];
const responseSignature = headers['signature']; // 后端生成的响应签名
// 组装入参
const signParamResponseTest = {
timestamp: responseTimestamp, // 后端生成
secretKey: 'bd17162bc09iu88', // 前端保存
code: data.code,
msg: data.msg,
data: data.data,
alert: data.alert
};
// 生成响应签名
const responseSignatureFront = generateSign(signParamResponseTest, 'MD5', 'Response')
// 验签
if(responseSignature === responseSignatureFront){
consle.log("有效的签名")
}