package signutil import ( "crypto/sha1" "encoding/hex" "sort" "strconv" "strings" "time" validation "github.com/go-ozzo/ozzo-validation/v4" "gogs.baozhida.cn/Cold_Logistic_libs/pkg/contrib/errors" ) const ( AppidKey = "appid" NoncestrKey = "noncestr" TimestampKey = "timestamp" SignKey = "sign" ) type GenSignFunc func(appid, secret, nonceStr, timestamp string) string type IBINGLIDigest struct { AppId string NonceStr string Timestamp string Sign string TimeLimit time.Duration genSignFunc GenSignFunc } type IBINGLIDigestOpt func(d *IBINGLIDigest) func WithIBINGLIDigestTimeLimit(t time.Duration) IBINGLIDigestOpt { return func(d *IBINGLIDigest) { d.TimeLimit = t } } func WithIBINGLIDigestGenSignFunc(f GenSignFunc) IBINGLIDigestOpt { return func(d *IBINGLIDigest) { d.genSignFunc = f } } func NewIBINGLIDigestByParam(param map[string]string, opts ...IBINGLIDigestOpt) *IBINGLIDigest { d := &IBINGLIDigest{ AppId: param[AppidKey], NonceStr: param[NoncestrKey], Timestamp: param[TimestampKey], Sign: param[SignKey], genSignFunc: defaultGenSign, } for _, opt := range opts { opt(d) } return d } func NewIBINGLIDigest(appId, nonceStr, timestamp, sign string, opts ...IBINGLIDigestOpt) *IBINGLIDigest { d := &IBINGLIDigest{ AppId: appId, NonceStr: nonceStr, Timestamp: timestamp, Sign: sign, genSignFunc: defaultGenSign, } for _, opt := range opts { opt(d) } return d } func (v *IBINGLIDigest) Validate() error { err := validation.ValidateStruct(v, validation.Field(&v.AppId, validation.Required), validation.Field(&v.NonceStr, validation.Required), validation.Field(&v.Timestamp, validation.Required), validation.Field(&v.Sign, validation.Required), ) if err != nil { return err } if v.TimeLimit > 0 { return v.ValidateTimestamp() } return nil } // 验证时效性 func (v *IBINGLIDigest) ValidateTimestamp() error { ts, err := strconv.Atoi(v.Timestamp) if err != nil { return errors.WithStackOnce(err) } if time.Now().Sub(time.UnixMilli(int64(ts))) > v.TimeLimit { return errors.New("timestamp invalid") } return nil } func defaultGenSign(appid, secret, nonceStr, timestamp string) string { // /* // 签名规则: // 1. 拼接appid和secret字符串 // 2. 随机生成一个16位字符串,由[0~9a~zA~Z]组成 // 3. 使用当前13位时间戳(毫秒值) // 4. 拼接123中的字符串 // 5. 字典排序4中的字符串 // 6. 计算字符串的SHA1校验和 // */ target := appid + secret + nonceStr + timestamp targetStrArray := strings.Split(target, "") sort.Strings(targetStrArray) target = strings.Join(targetStrArray, "") h := sha1.New() h.Write([]byte(target)) sum := h.Sum(nil) return hex.EncodeToString(sum) } // 生成签名 func (v *IBINGLIDigest) GenSign(secret string) string { return v.genSignFunc(v.AppId, secret, v.NonceStr, v.Timestamp) } // 比较签名 func (v *IBINGLIDigest) CompareSign(secret string) bool { return v.Sign == v.GenSign(secret) }