在 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 }))
签名生成规则
需要在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 生成规则
- 将type=? ,key =base64(私钥密码),time=当前时间戳 以 ; 分割成字符串
- type => 取值范围:0或1,
- key => 私钥密码的base64值
- time => 当前时间戳
- 再用公钥对所得的字符串进行 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 生成规则
- 将 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加密
- 再将此字符串用你的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; };