# 签名算法
接入方访问文档中台在线预览编辑服务的接口时,都需要在请求头添加 Authorization
、nonce
、timestamp
三个字段。其中,Authorization
字段由 ${appId}:${signature}
组成,中间用半角冒号(英文输入模式)隔开;nonce
是长度为 6 的一串随机数,例如 413410 ;timestamp
是当前时间戳(秒),例如 1619078626。appId
由文档中台提供,每个接入方有唯一的 appId
和相应的AppSecret
。 signature
通过下述签名算法生成。
签名参数:
key | value示例 | 说明 |
---|---|---|
appId | dd379d6c | appId |
method | GET | 请求方式,大写 |
nonce | 123adf456aof2131ew | 10-32 位随机字符串 |
uri | /api/edit&fid=JHhjABmSbKiy2Oujkq2 | 请求路径。此路径包含query参数,参数顺序同实际调用顺序。 |
body | dd4034cf589875fee94d34410d98e67a | 对请求体转字符串取md5值。GET请求body不参与签名计算。form-data中的file使用md5值作为value,对参数key进行字典排序,使用& =拼接参数转为queryString格式字符串,最后对拼接字符串取md5值。 |
timestamp | 1619078626 | 时间戳(秒) |
将上述参数按照 key
的字典顺序(升序)来排序,key
和 value
之间用 =
连接,每个参数之间用 &
连接,得到如下字符串:
appId=dd379d6c&method=GET&nonce=123adf456aof2131ew×tamp=1619078626&uri=%2Fapi%2Fedit%26fid%3DJHhjABmSbKiy2Oujkq2
接着对上一步得到的字符串使用 HmacSHA1
算法加密,秘钥为 AppSecret
,对结果使用 base64
编码。
例如,对于上述的例子,如果对应的 AppSecret = bb84cd4a6a123632ce2be787c955ac0e
,则通过签名算法得到的签名为 vxX3aZ2Y4rFMjkNrSrY/AVIOLeA=
。
示例代码:
public static String signature(String accessKeySecret, SignModel signModel) throws Exception {
Map<String,String> signMap = JSON.parseObject(JSON.toJSONString(signModel), new TypeReference<Map<String,String>>(){});
String baseString = getQueryString(signMap);
log.info("baseString:" + baseString);
Mac mac = Mac.getInstance("HmacSHA1");
SecretKeySpec keySpec = new SecretKeySpec(accessKeySecret.getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8.name());
mac.init(keySpec);
byte[] signBytes = mac.doFinal(baseString.getBytes(StandardCharsets.UTF_8));
log.info(new String(signBytes,"utf-8"));
log.info(bytesToHex(signBytes));
return Base64.getEncoder().encodeToString(signBytes);
}
public static String bytesToHex(byte[] bytes) {
StringBuffer sb = new StringBuffer();
for(int i = 0; i < bytes.length; i++) {
String hex = Integer.toHexString(bytes[i] & 0xFF);
if(hex.length() < 2){
sb.append(0);
}
sb.append(hex);
}
return sb.toString();
}
public static String getQueryString(Map<String, String> queryParam) throws UnsupportedEncodingException {
TreeMap<String, String> treeMap = new TreeMap<>(queryParam);
StringBuilder builder = new StringBuilder();
for (Map.Entry<String, String> entry : treeMap.entrySet()) {
String value = entry.getValue();
if (value != null && !value.isEmpty()) {
String encode = URLEncoder.encode(value, StandardCharsets.UTF_8.name());
builder.append(entry.getKey()).append("=").append(encode).append("&");
}
}
if (builder.length() > 0) {
builder.deleteCharAt(builder.length() - 1);
}
return builder.toString();
}
中间用到的实体类SignModel.class
public class SignModel {
/**
* 应用唯一标识
*/
private String appId;
/**
* 调用方法
*/
private String method;
/**
* 随机字符,混淆签名
*/
private String nonce;
/**
* 请求地址(包含query参数)
*/
private String uri;
/**
* 请求体(json请求体取md5值。form-data请求参数转为queryString后取md5值, file参数转为md5值参与计算)
*/
private String body;
/**
* 秒级时间戳
*/
private String timestamp;
}
← 回调说明