mirror of
https://github.com/eiblog/eiblog.git
synced 2026-02-17 20:02:27 +08:00
172 lines
3.3 KiB
Go
172 lines
3.3 KiB
Go
package qbox
|
|
|
|
import (
|
|
"crypto/hmac"
|
|
"crypto/sha1"
|
|
"encoding/base64"
|
|
"io"
|
|
"net/http"
|
|
|
|
. "qiniupkg.com/api.v7/conf"
|
|
"qiniupkg.com/x/bytes.v7/seekable"
|
|
)
|
|
|
|
// ----------------------------------------------------------
|
|
|
|
type Mac struct {
|
|
AccessKey string
|
|
SecretKey []byte
|
|
}
|
|
|
|
func NewMac(accessKey, secretKey string) (mac *Mac) {
|
|
|
|
if accessKey == "" {
|
|
accessKey, secretKey = ACCESS_KEY, SECRET_KEY
|
|
}
|
|
return &Mac{accessKey, []byte(secretKey)}
|
|
}
|
|
|
|
func (mac *Mac) Sign(data []byte) (token string) {
|
|
|
|
h := hmac.New(sha1.New, mac.SecretKey)
|
|
h.Write(data)
|
|
|
|
sign := base64.URLEncoding.EncodeToString(h.Sum(nil))
|
|
return mac.AccessKey + ":" + sign[:27]
|
|
}
|
|
|
|
func (mac *Mac) SignWithData(b []byte) (token string) {
|
|
|
|
blen := base64.URLEncoding.EncodedLen(len(b))
|
|
|
|
key := mac.AccessKey
|
|
nkey := len(key)
|
|
ret := make([]byte, nkey+30+blen)
|
|
|
|
base64.URLEncoding.Encode(ret[nkey+30:], b)
|
|
|
|
h := hmac.New(sha1.New, mac.SecretKey)
|
|
h.Write(ret[nkey+30:])
|
|
digest := h.Sum(nil)
|
|
|
|
copy(ret, key)
|
|
ret[nkey] = ':'
|
|
base64.URLEncoding.Encode(ret[nkey+1:], digest)
|
|
ret[nkey+29] = ':'
|
|
|
|
return string(ret)
|
|
}
|
|
|
|
func (mac *Mac) SignRequest(req *http.Request, incbody bool) (token string, err error) {
|
|
|
|
h := hmac.New(sha1.New, mac.SecretKey)
|
|
|
|
u := req.URL
|
|
data := u.Path
|
|
if u.RawQuery != "" {
|
|
data += "?" + u.RawQuery
|
|
}
|
|
io.WriteString(h, data+"\n")
|
|
|
|
if incbody {
|
|
s2, err2 := seekable.New(req)
|
|
if err2 != nil {
|
|
return "", err2
|
|
}
|
|
h.Write(s2.Bytes())
|
|
}
|
|
|
|
sign := base64.URLEncoding.EncodeToString(h.Sum(nil))
|
|
token = mac.AccessKey + ":" + sign
|
|
return
|
|
}
|
|
|
|
func (mac *Mac) VerifyCallback(req *http.Request) (bool, error) {
|
|
|
|
auth := req.Header.Get("Authorization")
|
|
if auth == "" {
|
|
return false, nil
|
|
}
|
|
|
|
token, err := mac.SignRequest(req, true)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
return auth == "QBox "+token, nil
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
|
|
func Sign(mac *Mac, data []byte) string {
|
|
|
|
if mac == nil {
|
|
mac = NewMac(ACCESS_KEY, SECRET_KEY)
|
|
}
|
|
return mac.Sign(data)
|
|
}
|
|
|
|
func SignWithData(mac *Mac, data []byte) string {
|
|
|
|
if mac == nil {
|
|
mac = NewMac(ACCESS_KEY, SECRET_KEY)
|
|
}
|
|
return mac.SignWithData(data)
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
|
|
type Transport struct {
|
|
mac Mac
|
|
Transport http.RoundTripper
|
|
}
|
|
|
|
func incBody(req *http.Request) bool {
|
|
|
|
if req.Body == nil {
|
|
return false
|
|
}
|
|
if ct, ok := req.Header["Content-Type"]; ok {
|
|
switch ct[0] {
|
|
case "application/x-www-form-urlencoded":
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (t *Transport) NestedObject() interface{} {
|
|
|
|
return t.Transport
|
|
}
|
|
|
|
func (t *Transport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
|
|
|
|
token, err := t.mac.SignRequest(req, incBody(req))
|
|
if err != nil {
|
|
return
|
|
}
|
|
req.Header.Set("Authorization", "QBox "+token)
|
|
return t.Transport.RoundTrip(req)
|
|
}
|
|
|
|
func NewTransport(mac *Mac, transport http.RoundTripper) *Transport {
|
|
|
|
if mac == nil {
|
|
mac = NewMac(ACCESS_KEY, SECRET_KEY)
|
|
}
|
|
if transport == nil {
|
|
transport = http.DefaultTransport
|
|
}
|
|
t := &Transport{mac: *mac, Transport: transport}
|
|
return t
|
|
}
|
|
|
|
func NewClient(mac *Mac, transport http.RoundTripper) *http.Client {
|
|
|
|
t := NewTransport(mac, transport)
|
|
return &http.Client{Transport: t}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|