package auth import ( "bytes" "io/ioutil" "net/http" "net/url" "strings" "time" model "github.com/cloudreve/Cloudreve/v3/models" "github.com/cloudreve/Cloudreve/v3/pkg/conf" "github.com/cloudreve/Cloudreve/v3/pkg/serializer" "github.com/cloudreve/Cloudreve/v3/pkg/util" ) var ( ErrAuthFailed = serializer.NewError(serializer.CodeNoPermissionErr, "鉴权失败", nil) ErrExpired = serializer.NewError(serializer.CodeSignExpired, "签名已过期", nil) ) // General 通用的认证接口 var General Auth // Auth 鉴权认证 type Auth interface { // 对给定Body进行签名,expires为0表示永不过期 Sign(body string, expires int64) string // 对给定Body和Sign进行检查 Check(body string, sign string) error } // SignRequest 对PUT\POST等复杂HTTP请求签名,如果请求Header中 // 包含 X-Policy, 则此请求会被认定为上传请求,只会对URI部分和 // Policy部分进行签名。其他请求则会对URI和Body部分进行签名。 func SignRequest(instance Auth, r *http.Request, expires int64) *http.Request { // 处理有效期 if expires > 0 { expires += time.Now().Unix() } // 生成签名 sign := instance.Sign(getSignContent(r), expires) // 将签名加到请求Header中 r.Header["Authorization"] = []string{"Bearer " + sign} return r } // CheckRequest 对复杂请求进行签名验证 func CheckRequest(instance Auth, r *http.Request) error { var ( sign []string ok bool ) if sign, ok = r.Header["Authorization"]; !ok || len(sign) == 0 { return ErrAuthFailed } sign[0] = strings.TrimPrefix(sign[0], "Bearer ") return instance.Check(getSignContent(r), sign[0]) } // getSignContent 根据请求Header中是否包含X-Policy判断是否为上传请求, // 返回待签名/验证的字符串 func getSignContent(r *http.Request) (rawSignString string) { if policy, ok := r.Header["X-Policy"]; ok { rawSignString = serializer.NewRequestSignString(r.URL.Path, policy[0], "") } else { var body = []byte{} if r.Body != nil { body, _ = ioutil.ReadAll(r.Body) _ = r.Body.Close() r.Body = ioutil.NopCloser(bytes.NewReader(body)) } rawSignString = serializer.NewRequestSignString(r.URL.Path, "", string(body)) } return rawSignString } // SignURI 对URI进行签名,签名只针对Path部分,query部分不做验证 func SignURI(instance Auth, uri string, expires int64) (*url.URL, error) { // 处理有效期 if expires != 0 { expires += time.Now().Unix() } base, err := url.Parse(uri) if err != nil { return nil, err } // 生成签名 sign := instance.Sign(base.Path, expires) // 将签名加到URI中 queries := base.Query() queries.Set("sign", sign) base.RawQuery = queries.Encode() return base, nil } // CheckURI 对URI进行鉴权 func CheckURI(instance Auth, url *url.URL) error { //获取待验证的签名正文 queries := url.Query() sign := queries.Get("sign") queries.Del("sign") url.RawQuery = queries.Encode() return instance.Check(url.Path, sign) } // Init 初始化通用鉴权器 func Init() { var secretKey string if conf.SystemConfig.Mode == "master" { secretKey = model.GetSettingByName("secret_key") } else { secretKey = conf.SlaveConfig.Secret if secretKey == "" { util.Log().Panic("未指定 SlaveSecret,请前往配置文件中指定") } } General = HMACAuth{ SecretKey: []byte(secretKey), } }