验签中间件.md 4.9 KB

验签中间件

go-zero 负责通过签名来验证请求是否合法的中间件:contentsecurityhandler 

密钥参数

params 参数说明
fingerprint 指纹字符串
publicKey 公钥
privateKey 私钥
key 私钥密码

如何使用

example

engine.AddRoute(rest.Route{
    Method:  http.MethodGet,
    Path:    "/",
    Handler: handle,
},rest.WithSignature(rest.SignatureConf{
    Strict:      true,      // 是否严格模式
    Expiry:      time.Hour, // 允许的最大时间跨度
    PrivateKeys: []rest.PrivateKeyConf{
        rest.PrivateKeyConf{
            Fingerprint: "", // your Fingerprint
            KeyFile:     "", // your PrivateKey
        },
    },
}))

自定义验签失败回调函数

engine := rest.MustNewServer(c.RestConf,rest.WithUnsignedCallback(func(w http.ResponseWriter, r *http.Request, next http.Handler, strict bool, code int) {
		// your own custom callback function
}))

签名生成规则

image.png

需要在http header中设置 X-Content-Security  值为 key=yourFingerprint;secret=yourSecret;signature=yourSignature

r.Header.Set("X-Content-Security", strings.Join([]string{
    fmt.Sprintf("key=%s", fingerprint),
    fmt.Sprintf("secret=%s", secret),
    fmt.Sprintf("signature=%s", signature),
}, "; "))
  • key 指纹字符字符串

取值为密钥参数中的 fingerprint 值

  • secret 生成规则

    1. 将type=? ,key =base64(私钥密码),time=当前时间戳  以 ; 分割成字符串
      • type => 取值范围:0或1,
      • key => 私钥密码的base64值
      • time =>  当前时间戳
    2. 再用公钥对所得的字符串进行 rsa加密 得到base64字符串
//1.将以上三个值以;分割
content := strings.Join([]string{
		fmt.Sprintf("type=%s", mode),
		fmt.Sprintf("key=%s", base64.StdEncoding.EncodeToString(key)), //对私钥密码做base64加密
		fmt.Sprintf("time=%s", strconv.FormatInt(time.Now().Unix(), 10)),
	}, "; ")
//用公钥对所得字符串进行rsa加密取base64值
encrypter, _ := codec.NewRsaEncrypter([]byte(pubKey))
output, _ := encrypter.Encrypt([]byte(content))
secret := base64.StdEncoding.EncodeToString(output)
  • signature  生成规则

    1. 将 timestamp,method,path,query,bodySign 的顺序 以换行(\n)分割成字符串
      • timestamp  当前时间戳
      • method  http 请求方式(GET/POST/PUT/....)
      • path  请求路径;例如 https://test.com/a/b?c=d&e=f   path 为 /a/b
      • query url参数 如上 query 为 c=d&e=f
      • bodySign 对request body 进行sha256加密
    2. 再将此字符串用你的key 做 hmac 后 再base64加密
//1.按timestamp,method,path,query,bodySign的顺序 以换行(\n)分割成字符串
str := strings.Join([]string{
  "1602745942",
  "POST",
  "/a/b",
  "c=d&e=f",
  "49adb43b1c44f36c046357da1ff548d739e89c8cc75cbe4536752c476dad0761"
}, "\n")

func Hmac(key []byte, body string) []byte {
	h := hmac.New(sha256.New, key)
	io.WriteString(h, body)
	return h.Sum(nil)
}

func HmacBase64(key []byte, body string) string {
	return base64.StdEncoding.EncodeToString(Hmac(key, body))
}
//2.然后再将此字符串用你的key做hmac后再base64加密
sign:= HmacBase64("yourKey",str)

js示例

/**
 * 签名
 * @param {*} url 相对路径的地址
 * @param {*} query url 参数
 * @param {*} method 请求方式
 * @param {*} data 请求体
 */
var httpSign = function (url, query, method, data) {
  	// 随机生成秘钥
    var key = uuid.v4(); 
  	// 请求体要是string
    var newData = typeof data === 'string' ? data : JSON.stringify(data);
		// 拿到当前时间戳
    var timestamp = Date.parse(new Date().toString()) / 1000;
  	// 对请求体做 sha256 加密
    var hexString = sha256(newData);
    var signatureData = [timestamp.toString(), method.toUpperCase(), url, query, hexString];
    var secretData = ["version=" + version, "type=0", "key=" + base64Encode(key), "time=" + timestamp];
  	// 使用公钥对 secretData 以;分割的字符串进行 rsa 加密
    var secret = rsaEncrypt(secretData.join(';'), PublicKey);
  	// 对signatureData 以 \n 分割的字符串先hmac在进行 sha256 加密
    var signature = hmacSHA256(signatureData.join('\n'), key);
  	// res 就是所得的放在请求头 X-Content-Security 中的签名字符串
    var res = "key=" + fingerprint + ";secret=" + secret + ";signature=" + signature;
    return res;
};