github.com/binbinly/pkg@v0.0.11-0.20240321014439-f4fbf666eb0f/signature/signature.go (about)

     1  package signature
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/hmac"
     6  	"crypto/sha256"
     7  	"net/url"
     8  	"strconv"
     9  	"time"
    10  
    11  	"github.com/binbinly/pkg/util"
    12  	"github.com/binbinly/pkg/util/xhash"
    13  )
    14  
    15  var _ Signature = (*signature)(nil)
    16  
    17  // CryptoFunc 签名加密函数
    18  type CryptoFunc func(b, k []byte) []byte
    19  
    20  const (
    21  	delimiter = "|"
    22  )
    23  
    24  type Signature interface {
    25  	// Generate 生成签名
    26  	Generate(params any) (auth string, ts int64, err error)
    27  	// Verify 验证签名
    28  	Verify(auth string, ts int64, params any) (ok bool, err error)
    29  }
    30  
    31  type signature struct {
    32  	key        string
    33  	secret     string
    34  	ttl        time.Duration
    35  	cryptoFunc CryptoFunc
    36  }
    37  
    38  func New(key, secret string, ttl time.Duration) Signature {
    39  	return &signature{
    40  		key:    key,
    41  		secret: secret,
    42  		ttl:    ttl,
    43  		cryptoFunc: func(b, k []byte) []byte {
    44  			buf := bytes.NewBuffer(b)
    45  			buf.WriteString("&key=")
    46  			buf.Write(k)
    47  			return xhash.MD5(buf.Bytes())
    48  		},
    49  	}
    50  }
    51  
    52  func NewSha256(key, secret string, ttl time.Duration) Signature {
    53  	return &signature{
    54  		key:    key,
    55  		secret: secret,
    56  		ttl:    ttl,
    57  		cryptoFunc: func(b, k []byte) []byte {
    58  			hash := hmac.New(sha256.New, k)
    59  			_, _ = hash.Write(b)
    60  			return hash.Sum(nil)
    61  		},
    62  	}
    63  }
    64  
    65  func NewCrypto(key, secret string, ttl time.Duration, f CryptoFunc) Signature {
    66  	return &signature{
    67  		key:        key,
    68  		secret:     secret,
    69  		ttl:        ttl,
    70  		cryptoFunc: f,
    71  	}
    72  }
    73  
    74  func (s *signature) data(timestamp int64, params any) ([]byte, error) {
    75  	buffer := bytes.NewBufferString(strconv.FormatInt(timestamp, 10))
    76  	buffer.WriteString(delimiter)
    77  	switch p := params.(type) {
    78  	case url.Values:
    79  		// Encode() 方法中自带 sorted by key
    80  		sortParamsEncode, err := url.QueryUnescape(p.Encode())
    81  		if err != nil {
    82  			return nil, err
    83  		}
    84  		buffer.WriteString(sortParamsEncode)
    85  	case map[string]any:
    86  		buffer.WriteString(util.MapBuildQuery(p))
    87  	case string:
    88  		buffer.WriteString(p)
    89  	}
    90  
    91  	return buffer.Bytes(), nil
    92  }