Skip to content

数字签名

概述

在微服务架构中,服务之间的安全通信是一项重要任务。为了验证发送方身份并确保数据在传递过程中不被篡改,我们希望使用加签和验签的机制来完成此项功能。

快速上手

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);

方法参数如下:

参数类型默认值描述
signParamSignParam签名算法密钥 secretKey; 时间戳 timestamp; 请求体 body; 请求参数 param; 响应数据 data; 响应码 code; 响应信息 msg; 告警提示码; alert
algorithmStringmd5加密算法 "md5" 或 "sha-256"
typeString签名类型 "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("有效的签名")
}