Skip to content

Latest commit

 

History

History
127 lines (104 loc) · 3.9 KB

SIGNATURE.md

File metadata and controls

127 lines (104 loc) · 3.9 KB

第三方开发者需要申请用户,分配appKey(eg: rain2103jds)和appSecret(eg: fea98ca429a311a2de3c60a356c29211)

将请求参数和提交的body(需要包含appKey)按照字典排序,然后跟uri和&appSecret拼接,没有则为空,做base64编码,将编码后的字符串中的'/'和'+'分别替换为‘_’和'-',然后进行HMAC1-SHA1加密,将得到的值再做base64编码和替换,最后通过url(GET)或者body(POST)传递过来。

appSecret不要出现在url里

示例1

假设请求为GET /api/test?user=123&role=student&op=submit

则参数按字典排序后为appkey=rain2103jds&op=submit&role=student&user=123

然后与/api/test?fea98ca429a311a2de3c60a356c29211连接后为/api/test?appkey=rain2103jds&op=submit&role=student&user=123&fea98ca429a311a2de3c60a356c29211,做base64编码和替换得到L2FwaS90ZXN0P2FwcGtleT1yYWluMjEwM2pkcyZvcD1zdWJtaXQmcm9sZT1zdHVkZW50JnVzZXI9MTIzJmZlYTk4Y2E0MjlhMzExYTJkZTNjNjBhMzU2YzI5MjEx,然后计算HMAC1-SHA1 hmac_sha1('L2FwaS90ZXN0P2FwcGtleT1yYWluMjEwM2pkcyZvcD1zdWJtaXQmcm9sZT1zdHVkZW50JnVzZXI9MTIzJmZlYTk4Y2E0MjlhMzExYTJkZTNjNjBhMzU2YzI5MjEx')得到GSlMLwhf5XZ1OJt6Yd/GXcjGkfQ=,将其再进行base64编码和替换,得到最终的签名 R1NsTUx3aGY1WFoxT0p0NllkL0dYY2pHa2ZRPQ==

最后将R1NsTUx3aGY1WFoxT0p0NllkL0dYY2pHa2ZRPQ==作为参数一起发送到服务器/api/test?user=123&role=student&op=submit&appKey=rain2103jds&signature=R1NsTUx3aGY1WFoxT0p0NllkL0dYY2pHa2ZRPQ==

示例2

假设请求为POST /api/test?test=123

{
  "user": 123,
  "role": "student",
  "op": "submit"
}

则参数按字典排序后拼接为test=123

body按字典排序后拼接为op=submit&role=student&user=123 与GET请求一样进行编码加密等操作,将最后的结果添加到body中。

发送请求/api/test?test=123到服务器

{
  "user": 123,
  "role": "student",
  "op": "submit",
  "signature": "MEFtQlA4T3VFcTJEdUhCakpGNzF6YVJndlNrPQ=="
}

下面是Node.js版本的签名算法

import crypto from 'crypto';

/**
 * 签名工具
 *
 * @param options
 *          options= {
 *              path: '',
 *              query: {},
 *              body: {},
 *              appSecret: ''
 *          }
 * @constructor
 */
class SignUtil {
    constructor(options = {}) {
        this.query = options.query;
        this.body = options.body;
        this.path = options.path;
        this.appSecret = options.appSecret || 'openplat';
    }
    /**
     * 计算签名
     *
     * @param appSecret APP密钥
     * @param path
     * @param query
     * @param body
     * @return 签名后的字符串
     */
    buildStringToSign () { 	
        //todo: 编码处理
        let queryStr = raw(this.query);
        let bodyStr = raw(this.body);
        let signStr = this.path + '?' + (queryStr ? (queryStr + '&') : '') + (bodyStr ? (bodyStr + '&') : '') + this.appSecret;

		let encodedFlags = this.urlsafeBase64Encode(signStr);
        let sign = this.hmacSha1(encodedFlags, this.appSecret);
	
        return this.urlsafeBase64Encode(sign);
    }

	urlsafeBase64Encode (str) {
		let encoded = new Buffer(str).toString('base64');
		return this.base64ToUrlSafe(encoded);
	}

	base64ToUrlSafe (v) {
		return v.replace(/\//g, '_').replace(/\+/g, '-');
	}

	hmacSha1 (encodedFlags, secretKey) {
		let hmac = crypto.createHmac('sha1', secretKey);
		hmac.update(encodedFlags);
		return hmac.digest('base64');
	}
	/**
     * 检查签名
     * @param signature 签名
     * @returns {boolean} 签名是否正确
     */
    checkSgin (signature = '') {
        return this.buildStringToSign() === signature;
    }

}

/*!
 * 排序查询字符串
 */
var raw = function (args) {
    if(!args || !Object.keys(args).length){
        return '';
    }
    let keys = Object.keys(args),  str = '';
    keys.sort().forEach(function (key) {
        str += '&' + key + '=' + args[key];
    });

    return str.slice(1);
};