Test: oss callback signature verification
This commit is contained in:
parent
5befbc21d0
commit
84a6218d3a
5 changed files with 493 additions and 4 deletions
|
@ -8,6 +8,7 @@ import (
|
||||||
"github.com/HFO4/cloudreve/models"
|
"github.com/HFO4/cloudreve/models"
|
||||||
"github.com/HFO4/cloudreve/pkg/auth"
|
"github.com/HFO4/cloudreve/pkg/auth"
|
||||||
"github.com/HFO4/cloudreve/pkg/cache"
|
"github.com/HFO4/cloudreve/pkg/cache"
|
||||||
|
"github.com/HFO4/cloudreve/pkg/filesystem/oss"
|
||||||
"github.com/HFO4/cloudreve/pkg/filesystem/upyun"
|
"github.com/HFO4/cloudreve/pkg/filesystem/upyun"
|
||||||
"github.com/HFO4/cloudreve/pkg/serializer"
|
"github.com/HFO4/cloudreve/pkg/serializer"
|
||||||
"github.com/HFO4/cloudreve/pkg/util"
|
"github.com/HFO4/cloudreve/pkg/util"
|
||||||
|
@ -206,14 +207,19 @@ func OSSCallbackAuth() gin.HandlerFunc {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO 验证OSS给出的签名
|
err := oss.VerifyCallbackSignature(c.Request)
|
||||||
|
if err != nil {
|
||||||
|
util.Log().Debug("回调签名验证失败,%s", err)
|
||||||
|
c.JSON(401, serializer.QiniuCallbackFailed{Error: "回调签名验证失败"})
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
c.Next()
|
c.Next()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpyunCallbackAuth 又拍云回调签名验证
|
// UpyunCallbackAuth 又拍云回调签名验证
|
||||||
// TODO 测试
|
|
||||||
func UpyunCallbackAuth() gin.HandlerFunc {
|
func UpyunCallbackAuth() gin.HandlerFunc {
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
// 验证key并查找用户
|
// 验证key并查找用户
|
||||||
|
@ -226,6 +232,7 @@ func UpyunCallbackAuth() gin.HandlerFunc {
|
||||||
|
|
||||||
// 获取请求正文
|
// 获取请求正文
|
||||||
body, err := ioutil.ReadAll(c.Request.Body)
|
body, err := ioutil.ReadAll(c.Request.Body)
|
||||||
|
c.Request.Body.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(401, serializer.QiniuCallbackFailed{Error: err.Error()})
|
c.JSON(401, serializer.QiniuCallbackFailed{Error: err.Error()})
|
||||||
c.Abort()
|
c.Abort()
|
||||||
|
|
|
@ -2,6 +2,7 @@ package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"errors"
|
||||||
"github.com/DATA-DOG/go-sqlmock"
|
"github.com/DATA-DOG/go-sqlmock"
|
||||||
"github.com/HFO4/cloudreve/models"
|
"github.com/HFO4/cloudreve/models"
|
||||||
"github.com/HFO4/cloudreve/pkg/auth"
|
"github.com/HFO4/cloudreve/pkg/auth"
|
||||||
|
@ -12,8 +13,10 @@ import (
|
||||||
"github.com/jinzhu/gorm"
|
"github.com/jinzhu/gorm"
|
||||||
"github.com/qiniu/api.v7/v7/auth/qbox"
|
"github.com/qiniu/api.v7/v7/auth/qbox"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -411,7 +414,7 @@ func TestOSSCallbackAuth(t *testing.T) {
|
||||||
asserts.True(c.IsAborted())
|
asserts.True(c.IsAborted())
|
||||||
}
|
}
|
||||||
|
|
||||||
// 成功
|
// 签名验证失败
|
||||||
{
|
{
|
||||||
cache.Set(
|
cache.Set(
|
||||||
"callback_testCallBackOSS",
|
"callback_testCallBackOSS",
|
||||||
|
@ -433,14 +436,183 @@ func TestOSSCallbackAuth(t *testing.T) {
|
||||||
c.Params = []gin.Param{
|
c.Params = []gin.Param{
|
||||||
{"key", "testCallBackOSS"},
|
{"key", "testCallBackOSS"},
|
||||||
}
|
}
|
||||||
c.Request, _ = http.NewRequest("POST", "/api/v3/callback/qiniu/testCallBackOSS", nil)
|
c.Request, _ = http.NewRequest("POST", "/api/v3/callback/oss/testCallBackOSS", nil)
|
||||||
mac := qbox.NewMac("123", "123")
|
mac := qbox.NewMac("123", "123")
|
||||||
token, err := mac.SignRequest(c.Request)
|
token, err := mac.SignRequest(c.Request)
|
||||||
asserts.NoError(err)
|
asserts.NoError(err)
|
||||||
c.Request.Header["Authorization"] = []string{"QBox " + token}
|
c.Request.Header["Authorization"] = []string{"QBox " + token}
|
||||||
AuthFunc(c)
|
AuthFunc(c)
|
||||||
asserts.NoError(mock.ExpectationsWereMet())
|
asserts.NoError(mock.ExpectationsWereMet())
|
||||||
|
asserts.True(c.IsAborted())
|
||||||
|
}
|
||||||
|
|
||||||
|
// 成功
|
||||||
|
{
|
||||||
|
cache.Set(
|
||||||
|
"callback_TnXx5E5VyfJUyM1UdkdDu1rtnJ34EbmH",
|
||||||
|
serializer.UploadSession{
|
||||||
|
UID: 1,
|
||||||
|
PolicyID: 2,
|
||||||
|
VirtualPath: "/",
|
||||||
|
},
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
cache.Deletes([]string{"1"}, "policy_")
|
||||||
|
mock.ExpectQuery("SELECT(.+)users(.+)").
|
||||||
|
WillReturnRows(sqlmock.NewRows([]string{"id", "group_id"}).AddRow(1, 1))
|
||||||
|
mock.ExpectQuery("SELECT(.+)groups(.+)").
|
||||||
|
WillReturnRows(sqlmock.NewRows([]string{"id", "policies"}).AddRow(1, "[2]"))
|
||||||
|
mock.ExpectQuery("SELECT(.+)policies(.+)").
|
||||||
|
WillReturnRows(sqlmock.NewRows([]string{"id", "access_key", "secret_key"}).AddRow(2, "123", "123"))
|
||||||
|
c, _ := gin.CreateTestContext(rec)
|
||||||
|
c.Params = []gin.Param{
|
||||||
|
{"key", "TnXx5E5VyfJUyM1UdkdDu1rtnJ34EbmH"},
|
||||||
|
}
|
||||||
|
c.Request, _ = http.NewRequest("POST", "/api/v3/callback/oss/TnXx5E5VyfJUyM1UdkdDu1rtnJ34EbmH", ioutil.NopCloser(strings.NewReader(`{"name":"2f7b2ccf30e9270ea920f1ab8a4037a546a2f0d5.jpg","source_name":"1/1_hFRtDLgM_2f7b2ccf30e9270ea920f1ab8a4037a546a2f0d5.jpg","size":114020,"pic_info":"810,539"}`)))
|
||||||
|
c.Request.Header["Authorization"] = []string{"e5LwzwTkP9AFAItT4YzvdJOHd0Y0wqTMWhsV/h5SG90JYGAmMd+8LQyj96R+9qUfJWjMt6suuUh7LaOryR87Dw=="}
|
||||||
|
c.Request.Header["X-Oss-Pub-Key-Url"] = []string{"aHR0cHM6Ly9nb3NzcHVibGljLmFsaWNkbi5jb20vY2FsbGJhY2tfcHViX2tleV92MS5wZW0="}
|
||||||
|
AuthFunc(c)
|
||||||
|
asserts.NoError(mock.ExpectationsWereMet())
|
||||||
asserts.False(c.IsAborted())
|
asserts.False(c.IsAborted())
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type fakeRead string
|
||||||
|
|
||||||
|
func (r fakeRead) Read(p []byte) (int, error) {
|
||||||
|
return 0, errors.New("error")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpyunCallbackAuth(t *testing.T) {
|
||||||
|
asserts := assert.New(t)
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
AuthFunc := UpyunCallbackAuth()
|
||||||
|
|
||||||
|
// Callback Key 相关验证失败
|
||||||
|
{
|
||||||
|
c, _ := gin.CreateTestContext(rec)
|
||||||
|
c.Params = []gin.Param{
|
||||||
|
{"key", "testUpyunBackRemote"},
|
||||||
|
}
|
||||||
|
c.Request, _ = http.NewRequest("POST", "/api/v3/callback/upyun/testUpyunBackRemote", nil)
|
||||||
|
AuthFunc(c)
|
||||||
|
asserts.True(c.IsAborted())
|
||||||
|
}
|
||||||
|
|
||||||
|
// 无法获取请求正文
|
||||||
|
{
|
||||||
|
cache.Set(
|
||||||
|
"callback_testCallBackUpyun",
|
||||||
|
serializer.UploadSession{
|
||||||
|
UID: 1,
|
||||||
|
PolicyID: 2,
|
||||||
|
VirtualPath: "/",
|
||||||
|
},
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
cache.Deletes([]string{"1"}, "policy_")
|
||||||
|
mock.ExpectQuery("SELECT(.+)users(.+)").
|
||||||
|
WillReturnRows(sqlmock.NewRows([]string{"id", "group_id"}).AddRow(1, 1))
|
||||||
|
mock.ExpectQuery("SELECT(.+)groups(.+)").
|
||||||
|
WillReturnRows(sqlmock.NewRows([]string{"id", "policies"}).AddRow(1, "[2]"))
|
||||||
|
mock.ExpectQuery("SELECT(.+)policies(.+)").
|
||||||
|
WillReturnRows(sqlmock.NewRows([]string{"id", "access_key", "secret_key"}).AddRow(2, "123", "123"))
|
||||||
|
c, _ := gin.CreateTestContext(rec)
|
||||||
|
c.Params = []gin.Param{
|
||||||
|
{"key", "testCallBackUpyun"},
|
||||||
|
}
|
||||||
|
c.Request, _ = http.NewRequest("POST", "/api/v3/callback/upyun/testCallBackUpyun", ioutil.NopCloser(fakeRead("")))
|
||||||
|
AuthFunc(c)
|
||||||
|
asserts.NoError(mock.ExpectationsWereMet())
|
||||||
|
asserts.True(c.IsAborted())
|
||||||
|
}
|
||||||
|
|
||||||
|
// 正文MD5不一致
|
||||||
|
{
|
||||||
|
cache.Set(
|
||||||
|
"callback_testCallBackUpyun",
|
||||||
|
serializer.UploadSession{
|
||||||
|
UID: 1,
|
||||||
|
PolicyID: 2,
|
||||||
|
VirtualPath: "/",
|
||||||
|
},
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
cache.Deletes([]string{"1"}, "policy_")
|
||||||
|
mock.ExpectQuery("SELECT(.+)users(.+)").
|
||||||
|
WillReturnRows(sqlmock.NewRows([]string{"id", "group_id"}).AddRow(1, 1))
|
||||||
|
mock.ExpectQuery("SELECT(.+)groups(.+)").
|
||||||
|
WillReturnRows(sqlmock.NewRows([]string{"id", "policies"}).AddRow(1, "[2]"))
|
||||||
|
mock.ExpectQuery("SELECT(.+)policies(.+)").
|
||||||
|
WillReturnRows(sqlmock.NewRows([]string{"id", "access_key", "secret_key"}).AddRow(2, "123", "123"))
|
||||||
|
c, _ := gin.CreateTestContext(rec)
|
||||||
|
c.Params = []gin.Param{
|
||||||
|
{"key", "testCallBackUpyun"},
|
||||||
|
}
|
||||||
|
c.Request, _ = http.NewRequest("POST", "/api/v3/callback/upyun/testCallBackUpyun", ioutil.NopCloser(strings.NewReader("1")))
|
||||||
|
c.Request.Header["Content-Md5"] = []string{"123"}
|
||||||
|
AuthFunc(c)
|
||||||
|
asserts.NoError(mock.ExpectationsWereMet())
|
||||||
|
asserts.True(c.IsAborted())
|
||||||
|
}
|
||||||
|
|
||||||
|
// 签名不一致
|
||||||
|
{
|
||||||
|
cache.Set(
|
||||||
|
"callback_testCallBackUpyun",
|
||||||
|
serializer.UploadSession{
|
||||||
|
UID: 1,
|
||||||
|
PolicyID: 2,
|
||||||
|
VirtualPath: "/",
|
||||||
|
},
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
cache.Deletes([]string{"1"}, "policy_")
|
||||||
|
mock.ExpectQuery("SELECT(.+)users(.+)").
|
||||||
|
WillReturnRows(sqlmock.NewRows([]string{"id", "group_id"}).AddRow(1, 1))
|
||||||
|
mock.ExpectQuery("SELECT(.+)groups(.+)").
|
||||||
|
WillReturnRows(sqlmock.NewRows([]string{"id", "policies"}).AddRow(1, "[2]"))
|
||||||
|
mock.ExpectQuery("SELECT(.+)policies(.+)").
|
||||||
|
WillReturnRows(sqlmock.NewRows([]string{"id", "access_key", "secret_key"}).AddRow(2, "123", "123"))
|
||||||
|
c, _ := gin.CreateTestContext(rec)
|
||||||
|
c.Params = []gin.Param{
|
||||||
|
{"key", "testCallBackUpyun"},
|
||||||
|
}
|
||||||
|
c.Request, _ = http.NewRequest("POST", "/api/v3/callback/upyun/testCallBackUpyun", ioutil.NopCloser(strings.NewReader("1")))
|
||||||
|
c.Request.Header["Content-Md5"] = []string{"c4ca4238a0b923820dcc509a6f75849b"}
|
||||||
|
AuthFunc(c)
|
||||||
|
asserts.NoError(mock.ExpectationsWereMet())
|
||||||
|
asserts.True(c.IsAborted())
|
||||||
|
}
|
||||||
|
|
||||||
|
// 成功
|
||||||
|
{
|
||||||
|
cache.Set(
|
||||||
|
"callback_testCallBackUpyun",
|
||||||
|
serializer.UploadSession{
|
||||||
|
UID: 1,
|
||||||
|
PolicyID: 2,
|
||||||
|
VirtualPath: "/",
|
||||||
|
},
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
cache.Deletes([]string{"1"}, "policy_")
|
||||||
|
mock.ExpectQuery("SELECT(.+)users(.+)").
|
||||||
|
WillReturnRows(sqlmock.NewRows([]string{"id", "group_id"}).AddRow(1, 1))
|
||||||
|
mock.ExpectQuery("SELECT(.+)groups(.+)").
|
||||||
|
WillReturnRows(sqlmock.NewRows([]string{"id", "policies"}).AddRow(1, "[2]"))
|
||||||
|
mock.ExpectQuery("SELECT(.+)policies(.+)").
|
||||||
|
WillReturnRows(sqlmock.NewRows([]string{"id", "access_key", "secret_key"}).AddRow(2, "123", "123"))
|
||||||
|
c, _ := gin.CreateTestContext(rec)
|
||||||
|
c.Params = []gin.Param{
|
||||||
|
{"key", "testCallBackUpyun"},
|
||||||
|
}
|
||||||
|
c.Request, _ = http.NewRequest("POST", "/api/v3/callback/upyun/testCallBackUpyun", ioutil.NopCloser(strings.NewReader("1")))
|
||||||
|
c.Request.Header["Content-Md5"] = []string{"c4ca4238a0b923820dcc509a6f75849b"}
|
||||||
|
c.Request.Header["Authorization"] = []string{"UPYUN 123:GWueK9x493BKFFk5gmfdO2Mn6EM="}
|
||||||
|
AuthFunc(c)
|
||||||
|
asserts.NoError(mock.ExpectationsWereMet())
|
||||||
|
asserts.False(c.IsAborted())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
116
pkg/filesystem/oss/callback.go
Normal file
116
pkg/filesystem/oss/callback.go
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
package oss
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto"
|
||||||
|
"crypto/md5"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/pem"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/HFO4/cloudreve/pkg/cache"
|
||||||
|
"github.com/HFO4/cloudreve/pkg/request"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetPublicKey 从回调请求或缓存中获取OSS的回调签名公钥
|
||||||
|
func GetPublicKey(r *http.Request) ([]byte, error) {
|
||||||
|
var pubKey []byte
|
||||||
|
|
||||||
|
// 尝试从缓存中获取
|
||||||
|
pub, exist := cache.Get("oss_public_key")
|
||||||
|
if exist {
|
||||||
|
return pub.([]byte), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从请求中获取
|
||||||
|
pubURL, err := base64.StdEncoding.DecodeString(r.Header.Get("x-oss-pub-key-url"))
|
||||||
|
if err != nil {
|
||||||
|
return pubKey, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保这个 public key 是由 OSS 颁发的
|
||||||
|
if !strings.HasPrefix(string(pubURL), "http://gosspublic.alicdn.com/") &&
|
||||||
|
!strings.HasPrefix(string(pubURL), "https://gosspublic.alicdn.com/") {
|
||||||
|
return pubKey, errors.New("公钥URL无效")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取公钥
|
||||||
|
client := request.HTTPClient{}
|
||||||
|
body, err := client.Request("GET", string(pubURL), nil).
|
||||||
|
CheckHTTPResponse(200).
|
||||||
|
GetResponse()
|
||||||
|
if err != nil {
|
||||||
|
return pubKey, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 写入缓存
|
||||||
|
_ = cache.Set("oss_public_key", []byte(body), 86400*7)
|
||||||
|
|
||||||
|
return []byte(body), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getRequestMD5(r *http.Request) ([]byte, error) {
|
||||||
|
var byteMD5 []byte
|
||||||
|
|
||||||
|
// 获取请求正文
|
||||||
|
body, err := ioutil.ReadAll(r.Body)
|
||||||
|
r.Body.Close()
|
||||||
|
if err != nil {
|
||||||
|
return byteMD5, err
|
||||||
|
}
|
||||||
|
r.Body = ioutil.NopCloser(bytes.NewReader(body))
|
||||||
|
|
||||||
|
strURLPathDecode, err := url.PathUnescape(r.URL.Path)
|
||||||
|
if err != nil {
|
||||||
|
return byteMD5, err
|
||||||
|
}
|
||||||
|
|
||||||
|
strAuth := fmt.Sprintf("%s\n%s", strURLPathDecode, string(body))
|
||||||
|
md5Ctx := md5.New()
|
||||||
|
md5Ctx.Write([]byte(strAuth))
|
||||||
|
byteMD5 = md5Ctx.Sum(nil)
|
||||||
|
|
||||||
|
return byteMD5, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyCallbackSignature 验证OSS回调请求
|
||||||
|
func VerifyCallbackSignature(r *http.Request) error {
|
||||||
|
bytePublicKey, err := GetPublicKey(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
byteMD5, err := getRequestMD5(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
strAuthorizationBase64 := r.Header.Get("authorization")
|
||||||
|
if strAuthorizationBase64 == "" {
|
||||||
|
return errors.New("no authorization field in Request header")
|
||||||
|
}
|
||||||
|
authorization, _ := base64.StdEncoding.DecodeString(strAuthorizationBase64)
|
||||||
|
|
||||||
|
pubBlock, _ := pem.Decode(bytePublicKey)
|
||||||
|
if pubBlock == nil {
|
||||||
|
return errors.New("pubBlock not exist")
|
||||||
|
}
|
||||||
|
pubInterface, err := x509.ParsePKIXPublicKey(pubBlock.Bytes)
|
||||||
|
if (pubInterface == nil) || (err != nil) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pub := pubInterface.(*rsa.PublicKey)
|
||||||
|
|
||||||
|
errorVerifyPKCS1v15 := rsa.VerifyPKCS1v15(pub, crypto.MD5, byteMD5, authorization)
|
||||||
|
if errorVerifyPKCS1v15 != nil {
|
||||||
|
return errorVerifyPKCS1v15
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
192
pkg/filesystem/oss/callback_test.go
Normal file
192
pkg/filesystem/oss/callback_test.go
Normal file
|
@ -0,0 +1,192 @@
|
||||||
|
package oss
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/HFO4/cloudreve/pkg/cache"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetPublicKey(t *testing.T) {
|
||||||
|
asserts := assert.New(t)
|
||||||
|
testCases := []struct {
|
||||||
|
Request http.Request
|
||||||
|
ResNil bool
|
||||||
|
Error bool
|
||||||
|
}{
|
||||||
|
// Header解码失败
|
||||||
|
{
|
||||||
|
Request: http.Request{
|
||||||
|
Header: http.Header{
|
||||||
|
"X-Oss-Pub-Key-Url": {"中文"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ResNil: true,
|
||||||
|
Error: true,
|
||||||
|
},
|
||||||
|
// 公钥URL无效
|
||||||
|
{
|
||||||
|
Request: http.Request{
|
||||||
|
Header: http.Header{
|
||||||
|
"X-Oss-Pub-Key-Url": {"aHR0cHM6Ly9wb3JuaHViLmNvbQ=="},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ResNil: true,
|
||||||
|
Error: true,
|
||||||
|
},
|
||||||
|
// 请求失败
|
||||||
|
{
|
||||||
|
Request: http.Request{
|
||||||
|
Header: http.Header{
|
||||||
|
"X-Oss-Pub-Key-Url": {"aHR0cDovL2dvc3NwdWJsaWMuYWxpY2RuLmNvbS8yMzQyMzQ="},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ResNil: true,
|
||||||
|
Error: true,
|
||||||
|
},
|
||||||
|
// 成功
|
||||||
|
{
|
||||||
|
Request: http.Request{
|
||||||
|
Header: http.Header{
|
||||||
|
"X-Oss-Pub-Key-Url": {"aHR0cDovL2dvc3NwdWJsaWMuYWxpY2RuLmNvbS9jYWxsYmFja19wdWJfa2V5X3YxLnBlbQ=="},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ResNil: false,
|
||||||
|
Error: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, testCase := range testCases {
|
||||||
|
asserts.NoError(cache.Deletes([]string{"oss_public_key"}, ""))
|
||||||
|
res, err := GetPublicKey(&testCase.Request)
|
||||||
|
if testCase.Error {
|
||||||
|
asserts.Error(err, "Test Case #%d", i)
|
||||||
|
} else {
|
||||||
|
asserts.NoError(err, "Test Case #%d", i)
|
||||||
|
}
|
||||||
|
if testCase.ResNil {
|
||||||
|
asserts.Empty(res, "Test Case #%d", i)
|
||||||
|
} else {
|
||||||
|
asserts.NotEmpty(res, "Test Case #%d", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试缓存
|
||||||
|
asserts.NoError(cache.Set("oss_public_key", []byte("123"), 0))
|
||||||
|
res, err := GetPublicKey(nil)
|
||||||
|
asserts.NoError(err)
|
||||||
|
asserts.Equal([]byte("123"), res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVerifyCallbackSignature(t *testing.T) {
|
||||||
|
asserts := assert.New(t)
|
||||||
|
testPubKey := `-----BEGIN PUBLIC KEY-----
|
||||||
|
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKs/JBGzwUB2aVht4crBx3oIPBLNsjGs
|
||||||
|
C0fTXv+nvlmklvkcolvpvXLTjaxUHR3W9LXxQ2EHXAJfCB+6H2YF1k8CAwEAAQ==
|
||||||
|
-----END PUBLIC KEY-----
|
||||||
|
`
|
||||||
|
|
||||||
|
// 成功
|
||||||
|
{
|
||||||
|
asserts.NoError(cache.Set("oss_public_key", []byte(testPubKey), 0))
|
||||||
|
r := http.Request{
|
||||||
|
URL: &url.URL{Path: "/api/v3/callback/oss/TnXx5E5VyfJUyM1UdkdDu1rtnJ34EbmH"},
|
||||||
|
Header: map[string][]string{
|
||||||
|
"Authorization": {"e5LwzwTkP9AFAItT4YzvdJOHd0Y0wqTMWhsV/h5SG90JYGAmMd+8LQyj96R+9qUfJWjMt6suuUh7LaOryR87Dw=="},
|
||||||
|
"X-Oss-Pub-Key-Url": {"aHR0cHM6Ly9nb3NzcHVibGljLmFsaWNkbi5jb20vY2FsbGJhY2tfcHViX2tleV92MS5wZW0="},
|
||||||
|
},
|
||||||
|
Body: ioutil.NopCloser(strings.NewReader(`{"name":"2f7b2ccf30e9270ea920f1ab8a4037a546a2f0d5.jpg","source_name":"1/1_hFRtDLgM_2f7b2ccf30e9270ea920f1ab8a4037a546a2f0d5.jpg","size":114020,"pic_info":"810,539"}`)),
|
||||||
|
}
|
||||||
|
asserts.NoError(VerifyCallbackSignature(&r))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 签名错误
|
||||||
|
{
|
||||||
|
asserts.NoError(cache.Set("oss_public_key", []byte(testPubKey), 0))
|
||||||
|
r := http.Request{
|
||||||
|
URL: &url.URL{Path: "/api/v3/callback/oss/TnXx5E5VyfJUyM1UdkdDu1rtnJ34EbmH"},
|
||||||
|
Header: map[string][]string{
|
||||||
|
"Authorization": {"e3LwzwTkP9AFAItT4YzvdJOHd0Y0wqTMWhsV/h5SG90JYGAmMd+8LQyj96R+9qUfJWjMt6suuUh7LaOryR87Dw=="},
|
||||||
|
"X-Oss-Pub-Key-Url": {"aHR0cHM6Ly9nb3NzcHVibGljLmFsaWNkbi5jb20vY2FsbGJhY2tfcHViX2tleV92MS5wZW0="},
|
||||||
|
},
|
||||||
|
Body: ioutil.NopCloser(strings.NewReader(`{"name":"2f7b2ccf30e9270ea920f1ab8a4037a546a2f0d5.jpg","source_name":"1/1_hFRtDLgM_2f7b2ccf30e9270ea920f1ab8a4037a546a2f0d5.jpg","size":114020,"pic_info":"810,539"}`)),
|
||||||
|
}
|
||||||
|
asserts.Error(VerifyCallbackSignature(&r))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPubKey 失败
|
||||||
|
{
|
||||||
|
asserts.NoError(cache.Deletes([]string{"oss_public_key"}, ""))
|
||||||
|
r := http.Request{
|
||||||
|
URL: &url.URL{Path: "/api/v3/callback/oss/TnXx5E5VyfJUyM1UdkdDu1rtnJ34EbmH"},
|
||||||
|
Header: map[string][]string{
|
||||||
|
"Authorization": {"e5LwzwTkP9AFAItT4YzvdJOHd0Y0wqTMWhsV/h5SG90JYGAmMd+8LQyj96R+9qUfJWjMt6suuUh7LaOryR87Dw=="},
|
||||||
|
},
|
||||||
|
Body: ioutil.NopCloser(strings.NewReader(`{"name":"2f7b2ccf30e9270ea920f1ab8a4037a546a2f0d5.jpg","source_name":"1/1_hFRtDLgM_2f7b2ccf30e9270ea920f1ab8a4037a546a2f0d5.jpg","size":114020,"pic_info":"810,539"}`)),
|
||||||
|
}
|
||||||
|
asserts.Error(VerifyCallbackSignature(&r))
|
||||||
|
}
|
||||||
|
|
||||||
|
// getRequestMD5 失败
|
||||||
|
{
|
||||||
|
asserts.NoError(cache.Set("oss_public_key", []byte(testPubKey), 0))
|
||||||
|
r := http.Request{
|
||||||
|
URL: &url.URL{Path: "%测试"},
|
||||||
|
Header: map[string][]string{
|
||||||
|
"Authorization": {"e5LwzwTkP9AFAItT4YzvdJOHd0Y0wqTMWhsV/h5SG90JYGAmMd+8LQyj96R+9qUfJWjMt6suuUh7LaOryR87Dw=="},
|
||||||
|
"X-Oss-Pub-Key-Url": {"aHR0cHM6Ly9nb3NzcHVibGljLmFsaWNkbi5jb20vY2FsbGJhY2tfcHViX2tleV92MS5wZW0="},
|
||||||
|
},
|
||||||
|
Body: ioutil.NopCloser(strings.NewReader(`{"name":"2f7b2ccf30e9270ea920f1ab8a4037a546a2f0d5.jpg","source_name":"1/1_hFRtDLgM_2f7b2ccf30e9270ea920f1ab8a4037a546a2f0d5.jpg","size":114020,"pic_info":"810,539"}`)),
|
||||||
|
}
|
||||||
|
asserts.Error(VerifyCallbackSignature(&r))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 无 Authorization 头
|
||||||
|
{
|
||||||
|
asserts.NoError(cache.Set("oss_public_key", []byte(testPubKey), 0))
|
||||||
|
r := http.Request{
|
||||||
|
URL: &url.URL{Path: "/api/v3/callback/oss/TnXx5E5VyfJUyM1UdkdDu1rtnJ34EbmH"},
|
||||||
|
Header: map[string][]string{
|
||||||
|
"X-Oss-Pub-Key-Url": {"aHR0cHM6Ly9nb3NzcHVibGljLmFsaWNkbi5jb20vY2FsbGJhY2tfcHViX2tleV92MS5wZW0="},
|
||||||
|
},
|
||||||
|
Body: ioutil.NopCloser(strings.NewReader(`{"name":"2f7b2ccf30e9270ea920f1ab8a4037a546a2f0d5.jpg","source_name":"1/1_hFRtDLgM_2f7b2ccf30e9270ea920f1ab8a4037a546a2f0d5.jpg","size":114020,"pic_info":"810,539"}`)),
|
||||||
|
}
|
||||||
|
asserts.Error(VerifyCallbackSignature(&r))
|
||||||
|
}
|
||||||
|
|
||||||
|
// pub block 不存在
|
||||||
|
{
|
||||||
|
asserts.NoError(cache.Set("oss_public_key", []byte(""), 0))
|
||||||
|
r := http.Request{
|
||||||
|
URL: &url.URL{Path: "/api/v3/callback/oss/TnXx5E5VyfJUyM1UdkdDu1rtnJ34EbmH"},
|
||||||
|
Header: map[string][]string{
|
||||||
|
"Authorization": {"e5LwzwTkP9AFAItT4YzvdJOHd0Y0wqTMWhsV/h5SG90JYGAmMd+8LQyj96R+9qUfJWjMt6suuUh7LaOryR87Dw=="},
|
||||||
|
"X-Oss-Pub-Key-Url": {"aHR0cHM6Ly9nb3NzcHVibGljLmFsaWNkbi5jb20vY2FsbGJhY2tfcHViX2tleV92MS5wZW0="},
|
||||||
|
},
|
||||||
|
Body: ioutil.NopCloser(strings.NewReader(`{"name":"2f7b2ccf30e9270ea920f1ab8a4037a546a2f0d5.jpg","source_name":"1/1_hFRtDLgM_2f7b2ccf30e9270ea920f1ab8a4037a546a2f0d5.jpg","size":114020,"pic_info":"810,539"}`)),
|
||||||
|
}
|
||||||
|
asserts.Error(VerifyCallbackSignature(&r))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParsePKIXPublicKey出错
|
||||||
|
{
|
||||||
|
asserts.NoError(cache.Set("oss_public_key", []byte("-----BEGIN PUBLIC KEY-----\n-----END PUBLIC KEY-----"), 0))
|
||||||
|
r := http.Request{
|
||||||
|
URL: &url.URL{Path: "/api/v3/callback/oss/TnXx5E5VyfJUyM1UdkdDu1rtnJ34EbmH"},
|
||||||
|
Header: map[string][]string{
|
||||||
|
"Authorization": {"e5LwzwTkP9AFAItT4YzvdJOHd0Y0wqTMWhsV/h5SG90JYGAmMd+8LQyj96R+9qUfJWjMt6suuUh7LaOryR87Dw=="},
|
||||||
|
"X-Oss-Pub-Key-Url": {"aHR0cHM6Ly9nb3NzcHVibGljLmFsaWNkbi5jb20vY2FsbGJhY2tfcHViX2tleV92MS5wZW0="},
|
||||||
|
},
|
||||||
|
Body: ioutil.NopCloser(strings.NewReader(`{"name":"2f7b2ccf30e9270ea920f1ab8a4037a546a2f0d5.jpg","source_name":"1/1_hFRtDLgM_2f7b2ccf30e9270ea920f1ab8a4037a546a2f0d5.jpg","size":114020,"pic_info":"810,539"}`)),
|
||||||
|
}
|
||||||
|
asserts.Error(VerifyCallbackSignature(&r))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///api/v3/callback/oss/TnXx5E5VyfJUyM1UdkdDu1rtnJ34EbmH
|
||||||
|
//{"name":"2f7b2ccf30e9270ea920f1ab8a4037a546a2f0d5.jpg","source_name":"1/1_hFRtDLgM_2f7b2ccf30e9270ea920f1ab8a4037a546a2f0d5.jpg","size":114020,"pic_info":"810,539"}
|
||||||
|
// aHR0cHM6Ly9nb3NzcHVibGljLmFsaWNkbi5jb20vY2FsbGJhY2tfcHViX2tleV92MS5wZW0=
|
||||||
|
// e5LwzwTkP9AFAItT4YzvdJOHd0Y0wqTMWhsV/h5SG90JYGAmMd+8LQyj96R+9qUfJWjMt6suuUh7LaOryR87Dw==
|
|
@ -152,6 +152,8 @@ func (resp *Response) GetResponse() (string, error) {
|
||||||
return "", resp.Err
|
return "", resp.Err
|
||||||
}
|
}
|
||||||
respBody, err := ioutil.ReadAll(resp.Response.Body)
|
respBody, err := ioutil.ReadAll(resp.Response.Body)
|
||||||
|
_ = resp.Response.Body.Close()
|
||||||
|
|
||||||
return string(respBody), err
|
return string(respBody), err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue