mirror of
https://github.com/halejohn/Cloudreve.git
synced 2026-01-29 03:11:56 +08:00
Feat: sign http request / read running mode from config file
This commit is contained in:
@@ -2,7 +2,11 @@ package auth
|
||||
|
||||
import (
|
||||
model "github.com/HFO4/cloudreve/models"
|
||||
"github.com/HFO4/cloudreve/pkg/conf"
|
||||
"github.com/HFO4/cloudreve/pkg/serializer"
|
||||
"github.com/HFO4/cloudreve/pkg/util"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
@@ -22,6 +26,26 @@ type Auth interface {
|
||||
Check(body string, sign string) error
|
||||
}
|
||||
|
||||
// SignRequest 对PUT\POST等复杂HTTP请求签名,如果请求Header中
|
||||
// 包含 X-Policy, 则此请求会被认定为上传请求,只会对URI部分和
|
||||
// Policy部分进行签名。其他请求则会对URI和Body部分进行签名。
|
||||
func SignRequest(r *http.Request, expires int64) *http.Request {
|
||||
var rawSignString string
|
||||
if policy, ok := r.Header["X-Policy"]; ok {
|
||||
rawSignString = serializer.NewRequestSignString(r.URL.Path, policy[0], "")
|
||||
} else {
|
||||
body, _ := ioutil.ReadAll(r.Body)
|
||||
rawSignString = serializer.NewRequestSignString(r.URL.Path, "", string(body))
|
||||
}
|
||||
|
||||
// 生成签名
|
||||
sign := General.Sign(rawSignString, expires)
|
||||
|
||||
// 将签名加到请求Header中
|
||||
r.Header["Authorization"] = []string{"Bearer " + sign}
|
||||
return r
|
||||
}
|
||||
|
||||
// SignURI 对URI进行签名,签名只针对Path部分,query部分不做验证
|
||||
func SignURI(uri string, expires int64) (*url.URL, error) {
|
||||
base, err := url.Parse(uri)
|
||||
@@ -52,9 +76,18 @@ func CheckURI(url *url.URL) error {
|
||||
}
|
||||
|
||||
// Init 初始化通用鉴权器
|
||||
// TODO slave模式下从配置文件获取
|
||||
// TODO 测试
|
||||
func Init() {
|
||||
var secretKey string
|
||||
if conf.SystemConfig.Mode == "master" {
|
||||
secretKey = model.GetSettingByName("secret_key")
|
||||
} else {
|
||||
secretKey = conf.SystemConfig.SlaveSecret
|
||||
if secretKey == "" {
|
||||
util.Log().Panic("未指定 SlaveSecret,请前往配置文件中指定")
|
||||
}
|
||||
}
|
||||
General = HMACAuth{
|
||||
SecretKey: []byte(model.GetSettingByName("secret_key")),
|
||||
SecretKey: []byte(secretKey),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package auth
|
||||
import (
|
||||
"github.com/HFO4/cloudreve/pkg/util"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
@@ -46,3 +48,25 @@ func TestCheckURI(t *testing.T) {
|
||||
asserts.Error(CheckURI(sign))
|
||||
}
|
||||
}
|
||||
|
||||
func TestSignRequest(t *testing.T) {
|
||||
asserts := assert.New(t)
|
||||
General = HMACAuth{SecretKey: []byte(util.RandStringRunes(256))}
|
||||
|
||||
// 非上传请求
|
||||
{
|
||||
req, err := http.NewRequest("POST", "http://127.0.0.1/api/v3/upload", strings.NewReader("I am body."))
|
||||
asserts.NoError(err)
|
||||
req = SignRequest(req, 10)
|
||||
asserts.NotEmpty(req.Header["Authorization"])
|
||||
}
|
||||
|
||||
// 上传请求
|
||||
{
|
||||
req, err := http.NewRequest("POST", "http://127.0.0.1/api/v3/upload", strings.NewReader("I am body."))
|
||||
asserts.NoError(err)
|
||||
req.Header["X-Policy"] = []string{"I am Policy"}
|
||||
req = SignRequest(req, 10)
|
||||
asserts.NotEmpty(req.Header["Authorization"])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,8 +18,11 @@ type database struct {
|
||||
|
||||
// system 系统通用配置
|
||||
type system struct {
|
||||
Mode string `validate:"eq=master|eq=slave"`
|
||||
Listen string `validate:"required"`
|
||||
Debug bool
|
||||
SessionSecret string
|
||||
SlaveSecret string `validate:"omitempty,gte=64"`
|
||||
}
|
||||
|
||||
// captcha 验证码配置
|
||||
@@ -84,7 +87,7 @@ func Init(path string) {
|
||||
for sectionName, sectionStruct := range sections {
|
||||
err = mapSection(sectionName, sectionStruct)
|
||||
if err != nil {
|
||||
util.Log().Warning("配置文件 %s 分区解析失败: %s", sectionName, err)
|
||||
util.Log().Panic("配置文件 %s 分区解析失败: %s", sectionName, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,9 @@ var DatabaseConfig = &database{
|
||||
|
||||
// SystemConfig 系统公用配置
|
||||
var SystemConfig = &system{
|
||||
Debug: false,
|
||||
Debug: false,
|
||||
Mode: "master",
|
||||
Listen: ":5000",
|
||||
}
|
||||
|
||||
// CaptchaConfig 验证码配置
|
||||
|
||||
22
pkg/serializer/auth.go
Normal file
22
pkg/serializer/auth.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package serializer
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
// RequestRawSign 待签名的HTTP请求
|
||||
type RequestRawSign struct {
|
||||
Path string
|
||||
Policy string
|
||||
Body string
|
||||
}
|
||||
|
||||
// NewRequestSignString 返回JSON格式的待签名字符串
|
||||
// TODO 测试
|
||||
func NewRequestSignString(path, policy, body string) string {
|
||||
req := RequestRawSign{
|
||||
Path: path,
|
||||
Policy: policy,
|
||||
Body: body,
|
||||
}
|
||||
res, _ := json.Marshal(req)
|
||||
return string(res)
|
||||
}
|
||||
13
pkg/serializer/auth_test.go
Normal file
13
pkg/serializer/auth_test.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package serializer
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewRequestSignString(t *testing.T) {
|
||||
asserts := assert.New(t)
|
||||
|
||||
sign := NewRequestSignString("1", "2", "3")
|
||||
asserts.NotEmpty(sign)
|
||||
}
|
||||
Reference in New Issue
Block a user