Feat: sign request headers with all X- prefix

This commit is contained in:
HFO4 2021-08-21 11:05:40 +08:00
parent 177c7695e3
commit 6610064e24
6 changed files with 71 additions and 19 deletions

View file

@ -2,6 +2,7 @@ package auth
import (
"bytes"
"fmt"
"io/ioutil"
"net/http"
"net/url"
@ -30,9 +31,8 @@ type Auth interface {
Check(body string, sign string) error
}
// SignRequest 对PUT\POST等复杂HTTP请求签名如果请求Header中
// 包含 X-Policy 则此请求会被认定为上传请求只会对URI部分和
// Policy部分进行签名。其他请求则会对URI和Body部分进行签名。
// SignRequest 对PUT\POST等复杂HTTP请求签名只会对URI部分、
// 请求正文、`X-`开头的header进行签名
func SignRequest(instance Auth, r *http.Request, expires int64) *http.Request {
// 处理有效期
if expires > 0 {
@ -61,20 +61,28 @@ func CheckRequest(instance Auth, r *http.Request) error {
return instance.Check(getSignContent(r), sign[0])
}
// getSignContent 根据请求Header中是否包含X-Policy判断是否为上传请求
// getSignContent 签名请求path、正文、以`X-`开头的header
// 返回待签名/验证的字符串
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))
// 读取所有body正文
var body = []byte{}
if r.Body != nil {
body, _ = ioutil.ReadAll(r.Body)
_ = r.Body.Close()
r.Body = ioutil.NopCloser(bytes.NewReader(body))
}
// 决定要签名的header
var signedHeader []string
for k, _ := range r.Header {
if strings.HasPrefix(k, "X-") {
signedHeader = append(signedHeader, fmt.Sprintf("%s=%s", k, r.Header.Get(k)))
}
}
// 读取所有待签名Header
rawSignString = serializer.NewRequestSignString(r.URL.Path, strings.Join(signedHeader, "&"), string(body))
return rawSignString
}

View file

@ -12,6 +12,7 @@ import (
model "github.com/cloudreve/Cloudreve/v3/models"
"github.com/cloudreve/Cloudreve/v3/pkg/auth"
"github.com/cloudreve/Cloudreve/v3/pkg/conf"
"github.com/cloudreve/Cloudreve/v3/pkg/serializer"
"github.com/cloudreve/Cloudreve/v3/pkg/util"
)
@ -46,6 +47,7 @@ type options struct {
signTTL int64
ctx context.Context
contentLength int64
masterMeta bool
}
type optionFunc func(*options)
@ -110,6 +112,13 @@ func WithContentLength(s int64) Option {
})
}
// WithMasterMeta 请求时携带主机信息
func WithMasterMeta() Option {
return optionFunc(func(o *options) {
o.masterMeta = true
})
}
// Request 发送HTTP请求
func (c HTTPClient) Request(method, target string, body io.Reader, opts ...Option) *Response {
// 应用额外设置
@ -142,6 +151,12 @@ func (c HTTPClient) Request(method, target string, body io.Reader, opts ...Optio
// 添加请求相关设置
req.Header = options.header
if options.masterMeta {
req.Header.Add("X-Site-Url", model.GetSiteURL().String())
req.Header.Add("X-Cloudreve-Version", conf.BackendVersion)
}
if options.contentLength != -1 {
req.ContentLength = options.contentLength
}

View file

@ -5,16 +5,15 @@ import "encoding/json"
// RequestRawSign 待签名的HTTP请求
type RequestRawSign struct {
Path string
Policy string
Header string
Body string
}
// NewRequestSignString 返回JSON格式的待签名字符串
// TODO 测试
func NewRequestSignString(path, policy, body string) string {
func NewRequestSignString(path, header, body string) string {
req := RequestRawSign{
Path: path,
Policy: policy,
Header: header,
Body: body,
}
res, _ := json.Marshal(req)

View file

@ -10,6 +10,7 @@ import (
"github.com/cloudreve/Cloudreve/v3/pkg/filesystem/fsctx"
"github.com/cloudreve/Cloudreve/v3/pkg/serializer"
"github.com/cloudreve/Cloudreve/v3/service/admin"
"github.com/cloudreve/Cloudreve/v3/service/aria2"
"github.com/cloudreve/Cloudreve/v3/service/explorer"
"github.com/cloudreve/Cloudreve/v3/service/node"
"github.com/gin-gonic/gin"
@ -187,3 +188,14 @@ func SlaveHeartbeat(c *gin.Context) {
c.JSON(200, ErrorResponse(err))
}
}
// SlaveAria2Create 创建 Aria2 任务
func SlaveAria2Create(c *gin.Context) {
var service aria2.SlaveAria2Call
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Add(c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}

View file

@ -51,6 +51,12 @@ func InitSlaveRouter() *gin.Engine {
v3.POST("delete", controllers.SlaveDelete)
// 列出文件
v3.POST("list", controllers.SlaveList)
// 离线下载
aria2 := v3.Group("aria2")
{
aria2.POST("task", controllers.SlaveList)
}
}
return r
}

View file

@ -17,7 +17,14 @@ type AddURLService struct {
Dst string `json:"dst" binding:"required,min=1"`
}
// Add 创建新的链接离线下载任务
// SlaveAria2Call 从机有关Aria2的请求正文
type SlaveAria2Call struct {
Task *model.Download `json:"task"`
GroupOptions map[string]interface{} `json:"group_options"`
Files []uint `json:"files"`
}
// Add 主机创建新的链接离线下载任务
func (service *AddURLService) Add(c *gin.Context, taskType int) serializer.Response {
// 创建文件系统
fs, err := filesystem.NewFileSystemFromContext(c)
@ -74,3 +81,8 @@ func (service *AddURLService) Add(c *gin.Context, taskType int) serializer.Respo
return serializer.Response{}
}
// Add 从机创建新的链接离线下载任务
func (service *SlaveAria2Call) Add(c *gin.Context) serializer.Response {
return serializer.Response{}
}