# 验签中间件 在 `go-zero` 负责通过签名来验证请求是否合法的中间件:`contentsecurityhandler`  ## 密钥参数 | **params** | **参数说明** | | --- | --- | | fingerprint | 指纹字符串 | | publicKey | 公钥 | | privateKey | 私钥 | | key | 私钥密码 | ## 如何使用 ### example ```go 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 }, }, })) ``` ### 自定义验签失败回调函数 ```go 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](https://cdn.nlark.com/yuque/0/2020/png/112627/1603433428382-4e310d52-f07a-49f9-b676-e339b616baea.png#align=left&display=inline&height=906&margin=%5Bobject%20Object%5D&name=image.png&originHeight=906&originWidth=1274&size=112330&status=done&style=none&width=1274#align=left&display=inline&height=906&margin=%5Bobject%20Object%5D&originHeight=906&originWidth=1274&status=done&style=none&width=1274) 需要在http header中设置 X-Content-Security  值为 `key=yourFingerprint;secret=yourSecret;signature=yourSignature` ```go 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字符串 ```go //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](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加密 ```go //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示例 ```javascript /** * 签名 * @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; }; ```