@@ -0,0 +1,75 @@
|
||||
package searchaccessser
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"91porn-server/app/appg"
|
||||
"91porn-server/common/crypt"
|
||||
)
|
||||
|
||||
const (
|
||||
tokenScope = "video_search_access"
|
||||
tokenTTL = 10 * time.Minute
|
||||
)
|
||||
|
||||
// Issue 为搜索结果签发短时效的视频访问凭证。
|
||||
func Issue(uid uint64, videoID string, now time.Time) (string, error) {
|
||||
return issue(appg.Conf.Base.JwtKey, uid, videoID, now)
|
||||
}
|
||||
|
||||
// Validate 校验搜索结果访问凭证,凭证与用户和视频一一绑定。
|
||||
func Validate(token string, uid uint64, videoID string, now time.Time) bool {
|
||||
return validate(appg.Conf.Base.JwtKey, token, uid, videoID, now)
|
||||
}
|
||||
|
||||
func issue(secret string, uid uint64, videoID string, now time.Time) (string, error) {
|
||||
if videoID == "" {
|
||||
return "", errors.New("video id is empty")
|
||||
}
|
||||
return crypt.CreateToken(searchTokenSecret(secret), map[string]interface{}{
|
||||
"scope": tokenScope,
|
||||
"uid": uid,
|
||||
"vid": videoID,
|
||||
"iat": now.Unix(),
|
||||
"exp": now.Add(tokenTTL).Unix(),
|
||||
})
|
||||
}
|
||||
|
||||
func validate(secret, token string, uid uint64, videoID string, now time.Time) bool {
|
||||
claims, err := crypt.ParseToken(searchTokenSecret(secret), token)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
scope, _ := claims["scope"].(string)
|
||||
vid, _ := claims["vid"].(string)
|
||||
tokenUID, uidOK := numberClaim(claims["uid"])
|
||||
expiresAt, expOK := numberClaim(claims["exp"])
|
||||
return scope == tokenScope &&
|
||||
vid == videoID &&
|
||||
uidOK &&
|
||||
tokenUID == int64(uid) &&
|
||||
expOK &&
|
||||
expiresAt >= now.Unix()
|
||||
}
|
||||
|
||||
func searchTokenSecret(secret string) string {
|
||||
if secret == "" {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("%s:%s", secret, tokenScope)
|
||||
}
|
||||
|
||||
func numberClaim(value interface{}) (int64, bool) {
|
||||
switch number := value.(type) {
|
||||
case float64:
|
||||
return int64(number), true
|
||||
case int64:
|
||||
return number, true
|
||||
case int:
|
||||
return int64(number), true
|
||||
default:
|
||||
return 0, false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package searchaccessser
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestSearchAccessToken(t *testing.T) {
|
||||
const (
|
||||
secret = "unit-test-secret"
|
||||
uid = uint64(32239213)
|
||||
videoID = "64b100000000000000000001"
|
||||
)
|
||||
now := time.Now().Truncate(time.Second)
|
||||
token, err := issue(secret, uid, videoID, now)
|
||||
if err != nil {
|
||||
t.Fatalf("issue() error = %v", err)
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
secret string
|
||||
uid uint64
|
||||
video string
|
||||
now time.Time
|
||||
wantOK bool
|
||||
}{
|
||||
{name: "valid", secret: secret, uid: uid, video: videoID, now: now.Add(time.Minute), wantOK: true},
|
||||
{name: "wrong uid", secret: secret, uid: uid + 1, video: videoID, now: now.Add(time.Minute)},
|
||||
{name: "wrong video", secret: secret, uid: uid, video: "64b100000000000000000002", now: now.Add(time.Minute)},
|
||||
{name: "wrong secret", secret: "another-secret", uid: uid, video: videoID, now: now.Add(time.Minute)},
|
||||
{name: "expired", secret: secret, uid: uid, video: videoID, now: now.Add(tokenTTL + time.Second)},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
if got := validate(test.secret, token, test.uid, test.video, test.now); got != test.wantOK {
|
||||
t.Fatalf("validate() = %v, want %v", got, test.wantOK)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIssueRejectsMissingInputs(t *testing.T) {
|
||||
if _, err := issue("", 1, "64b100000000000000000001", time.Now()); err == nil {
|
||||
t.Fatal("issue() with empty secret should fail")
|
||||
}
|
||||
if _, err := issue("secret", 1, "", time.Now()); err == nil {
|
||||
t.Fatal("issue() with empty video id should fail")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user