鉴权增加缓存

This commit is contained in:
xmdhs 2023-09-12 13:19:13 +08:00
parent a3fa8cbe47
commit 599c8535fc
No known key found for this signature in database
GPG Key ID: E809D6D43DEFCC95
5 changed files with 78 additions and 25 deletions

30
db/cache/cache.go vendored
View File

@ -1,9 +1,37 @@
package cache package cache
import "time" import (
"encoding/json"
"time"
)
type Cache interface { type Cache interface {
Del(k []byte) error Del(k []byte) error
Get(k []byte) ([]byte, error) Get(k []byte) ([]byte, error)
Put(k []byte, v []byte, timeOut time.Time) error Put(k []byte, v []byte, timeOut time.Time) error
} }
type CacheHelp[T any] struct {
Cache
}
func (c CacheHelp[T]) Get(k []byte) (T, error) {
var t T
b, err := c.Cache.Get(k)
if err != nil {
return t, err
}
err = json.Unmarshal(b, &t)
if err != nil {
return t, err
}
return t, nil
}
func (c CacheHelp[T]) Put(k []byte, v T, timeOut time.Time) error {
b, err := json.Marshal(v)
if err != nil {
return err
}
return c.Cache.Put(k, b, timeOut)
}

View File

@ -9,6 +9,7 @@ import (
"time" "time"
"github.com/golang-jwt/jwt/v5" "github.com/golang-jwt/jwt/v5"
"github.com/xmdhs/authlib-skin/db/cache"
"github.com/xmdhs/authlib-skin/db/ent" "github.com/xmdhs/authlib-skin/db/ent"
"github.com/xmdhs/authlib-skin/db/ent/user" "github.com/xmdhs/authlib-skin/db/ent/user"
"github.com/xmdhs/authlib-skin/db/ent/usertoken" "github.com/xmdhs/authlib-skin/db/ent/usertoken"
@ -20,7 +21,7 @@ var (
ErrTokenInvalid = errors.New("token 无效") ErrTokenInvalid = errors.New("token 无效")
) )
func Auth(ctx context.Context, t yggdrasil.ValidateToken, client *ent.Client, pubkey *rsa.PublicKey, tmpInvalid bool) (*model.TokenClaims, error) { func Auth(ctx context.Context, t yggdrasil.ValidateToken, client *ent.Client, c cache.Cache, pubkey *rsa.PublicKey, tmpInvalid bool) (*model.TokenClaims, error) {
token, err := jwt.ParseWithClaims(t.AccessToken, &model.TokenClaims{}, func(t *jwt.Token) (interface{}, error) { token, err := jwt.ParseWithClaims(t.AccessToken, &model.TokenClaims{}, func(t *jwt.Token) (interface{}, error) {
return pubkey, nil return pubkey, nil
}) })
@ -50,16 +51,30 @@ func Auth(ctx context.Context, t yggdrasil.ValidateToken, client *ent.Client, pu
return nil, fmt.Errorf("Auth: %w", ErrTokenInvalid) return nil, fmt.Errorf("Auth: %w", ErrTokenInvalid)
} }
} }
tokenID, err := func() (uint64, error) {
c := cache.CacheHelp[uint64]{Cache: c}
key := []byte("auth" + strconv.Itoa(claims.UID))
t, err := c.Get(key)
if err != nil {
return 0, err
}
if t != 0 {
return t, nil
}
ut, err := client.UserToken.Query().Where(usertoken.HasUserWith(user.ID(claims.UID))).First(ctx) ut, err := client.UserToken.Query().Where(usertoken.HasUserWith(user.ID(claims.UID))).First(ctx)
if err != nil { if err != nil {
var ne *ent.NotFoundError var ne *ent.NotFoundError
if !errors.As(err, &ne) { if !errors.As(err, &ne) {
return nil, fmt.Errorf("Auth: %w", errors.Join(err, ErrTokenInvalid)) return 0, errors.Join(err, ErrTokenInvalid)
} }
return 0, err
}
return ut.TokenID, c.Put(key, ut.TokenID, time.Now().Add(20*time.Minute))
}()
if err != nil {
return nil, fmt.Errorf("Auth: %w", err) return nil, fmt.Errorf("Auth: %w", err)
} }
if strconv.FormatUint(ut.TokenID, 10) != claims.Tid { if strconv.FormatUint(tokenID, 10) != claims.Tid {
return nil, fmt.Errorf("Auth: %w", ErrTokenInvalid) return nil, fmt.Errorf("Auth: %w", ErrTokenInvalid)
} }
return claims, nil return claims, nil

View File

@ -2,11 +2,10 @@ package yggdrasil
import ( import (
"context" "context"
"encoding/json"
"fmt" "fmt"
"time" "time"
"github.com/samber/lo" "github.com/xmdhs/authlib-skin/db/cache"
"github.com/xmdhs/authlib-skin/db/ent/userprofile" "github.com/xmdhs/authlib-skin/db/ent/userprofile"
"github.com/xmdhs/authlib-skin/model" "github.com/xmdhs/authlib-skin/model"
"github.com/xmdhs/authlib-skin/model/yggdrasil" "github.com/xmdhs/authlib-skin/model/yggdrasil"
@ -21,17 +20,17 @@ type sessionWithIP struct {
func (y *Yggdrasil) SessionJoin(ctx context.Context, s yggdrasil.Session, ip string) error { func (y *Yggdrasil) SessionJoin(ctx context.Context, s yggdrasil.Session, ip string) error {
t, err := sutils.Auth(ctx, yggdrasil.ValidateToken{ t, err := sutils.Auth(ctx, yggdrasil.ValidateToken{
AccessToken: s.AccessToken, AccessToken: s.AccessToken,
}, y.client, &y.prikey.PublicKey, true) }, y.client, y.cache, &y.prikey.PublicKey, true)
if err != nil { if err != nil {
return fmt.Errorf("SessionJoin: %w", err) return fmt.Errorf("SessionJoin: %w", err)
} }
if s.SelectedProfile != t.Subject { if s.SelectedProfile != t.Subject {
return fmt.Errorf("SessionJoin: %w", sutils.ErrTokenInvalid) return fmt.Errorf("SessionJoin: %w", sutils.ErrTokenInvalid)
} }
err = y.cache.Put([]byte("session"+s.ServerID), lo.Must1(json.Marshal(sessionWithIP{ err = cache.CacheHelp[sessionWithIP]{Cache: y.cache}.Put([]byte("session"+s.ServerID), sessionWithIP{
user: *t, user: *t,
IP: ip, IP: ip,
})), time.Now().Add(30*time.Second)) }, time.Now().Add(30*time.Second))
if err != nil { if err != nil {
return fmt.Errorf("SessionJoin: %w", err) return fmt.Errorf("SessionJoin: %w", err)
} }
@ -39,10 +38,10 @@ func (y *Yggdrasil) SessionJoin(ctx context.Context, s yggdrasil.Session, ip str
} }
func (y *Yggdrasil) HasJoined(ctx context.Context, username, serverId string, ip string, host string) (yggdrasil.UserInfo, error) { func (y *Yggdrasil) HasJoined(ctx context.Context, username, serverId string, ip string, host string) (yggdrasil.UserInfo, error) {
b := lo.Must1(y.cache.Get([]byte("session" + serverId))) sIP, err := cache.CacheHelp[sessionWithIP]{Cache: y.cache}.Get([]byte("session" + serverId))
sIP := sessionWithIP{} if err != nil {
lo.Must0(json.Unmarshal(b, &sIP)) return yggdrasil.UserInfo{}, fmt.Errorf("HasJoined: %w", err)
}
if ip != "" && ip != sIP.IP { if ip != "" && ip != sIP.IP {
return yggdrasil.UserInfo{}, fmt.Errorf("ip 不相同") return yggdrasil.UserInfo{}, fmt.Errorf("ip 不相同")
} }

View File

@ -79,7 +79,7 @@ func (y *Yggdrasil) delTexture(ctx context.Context, userProfileID int, textureTy
} }
func (y *Yggdrasil) DelTexture(ctx context.Context, uuid string, token string, textureType string) error { func (y *Yggdrasil) DelTexture(ctx context.Context, uuid string, token string, textureType string) error {
t, err := utilsService.Auth(ctx, yggdrasil.ValidateToken{AccessToken: token}, y.client, &y.prikey.PublicKey, true) t, err := utilsService.Auth(ctx, yggdrasil.ValidateToken{AccessToken: token}, y.client, y.cache, &y.prikey.PublicKey, true)
if err != nil { if err != nil {
return fmt.Errorf("DelTexture: %w", err) return fmt.Errorf("DelTexture: %w", err)
} }
@ -98,7 +98,7 @@ func (y *Yggdrasil) DelTexture(ctx context.Context, uuid string, token string, t
} }
func (y *Yggdrasil) PutTexture(ctx context.Context, token string, texturebyte []byte, model string, uuid string, textureType string) error { func (y *Yggdrasil) PutTexture(ctx context.Context, token string, texturebyte []byte, model string, uuid string, textureType string) error {
t, err := utilsService.Auth(ctx, yggdrasil.ValidateToken{AccessToken: token}, y.client, &y.prikey.PublicKey, true) t, err := utilsService.Auth(ctx, yggdrasil.ValidateToken{AccessToken: token}, y.client, y.cache, &y.prikey.PublicKey, true)
if err != nil { if err != nil {
return fmt.Errorf("PutTexture: %w", err) return fmt.Errorf("PutTexture: %w", err)
} }

View File

@ -85,7 +85,10 @@ func (y *Yggdrasil) Authenticate(cxt context.Context, auth yggdrasil.Authenticat
if err != nil { if err != nil {
return yggdrasil.Token{}, fmt.Errorf("Authenticate: %w", err) return yggdrasil.Token{}, fmt.Errorf("Authenticate: %w", err)
} }
err = y.cache.Del([]byte("auth" + strconv.Itoa(u.ID)))
if err != nil {
return yggdrasil.Token{}, fmt.Errorf("Authenticate: %w", err)
}
jwts, err := newJwtToken(y.prikey, strconv.FormatUint(utoken.TokenID, 10), clientToken, u.Edges.Profile.UUID, u.ID) jwts, err := newJwtToken(y.prikey, strconv.FormatUint(utoken.TokenID, 10), clientToken, u.Edges.Profile.UUID, u.ID)
if err != nil { if err != nil {
return yggdrasil.Token{}, fmt.Errorf("Authenticate: %w", err) return yggdrasil.Token{}, fmt.Errorf("Authenticate: %w", err)
@ -107,7 +110,7 @@ func (y *Yggdrasil) Authenticate(cxt context.Context, auth yggdrasil.Authenticat
} }
func (y *Yggdrasil) ValidateToken(ctx context.Context, t yggdrasil.ValidateToken) error { func (y *Yggdrasil) ValidateToken(ctx context.Context, t yggdrasil.ValidateToken) error {
_, err := sutils.Auth(ctx, t, y.client, &y.prikey.PublicKey, true) _, err := sutils.Auth(ctx, t, y.client, y.cache, &y.prikey.PublicKey, true)
if err != nil { if err != nil {
return fmt.Errorf("ValidateToken: %w", err) return fmt.Errorf("ValidateToken: %w", err)
} }
@ -131,11 +134,15 @@ func (y *Yggdrasil) SignOut(ctx context.Context, t yggdrasil.Pass) error {
if err != nil { if err != nil {
return fmt.Errorf("SignOut: %w", err) return fmt.Errorf("SignOut: %w", err)
} }
err = y.cache.Del([]byte("auth" + strconv.Itoa(u.ID)))
if err != nil {
return fmt.Errorf("SignOut: %w", err)
}
return nil return nil
} }
func (y *Yggdrasil) Invalidate(ctx context.Context, accessToken string) error { func (y *Yggdrasil) Invalidate(ctx context.Context, accessToken string) error {
t, err := sutils.Auth(ctx, yggdrasil.ValidateToken{AccessToken: accessToken}, y.client, &y.prikey.PublicKey, true) t, err := sutils.Auth(ctx, yggdrasil.ValidateToken{AccessToken: accessToken}, y.client, y.cache, &y.prikey.PublicKey, true)
if err != nil { if err != nil {
return fmt.Errorf("Invalidate: %w", err) return fmt.Errorf("Invalidate: %w", err)
} }
@ -143,11 +150,15 @@ func (y *Yggdrasil) Invalidate(ctx context.Context, accessToken string) error {
if err != nil { if err != nil {
return fmt.Errorf("Invalidate: %w", err) return fmt.Errorf("Invalidate: %w", err)
} }
err = y.cache.Del([]byte("auth" + strconv.Itoa(t.UID)))
if err != nil {
return fmt.Errorf("Invalidate: %w", err)
}
return nil return nil
} }
func (y *Yggdrasil) Refresh(ctx context.Context, token yggdrasil.RefreshToken) (yggdrasil.Token, error) { func (y *Yggdrasil) Refresh(ctx context.Context, token yggdrasil.RefreshToken) (yggdrasil.Token, error) {
t, err := sutils.Auth(ctx, yggdrasil.ValidateToken{AccessToken: token.AccessToken, ClientToken: token.ClientToken}, y.client, &y.prikey.PublicKey, false) t, err := sutils.Auth(ctx, yggdrasil.ValidateToken{AccessToken: token.AccessToken, ClientToken: token.ClientToken}, y.client, y.cache, &y.prikey.PublicKey, false)
if err != nil { if err != nil {
return yggdrasil.Token{}, fmt.Errorf("Refresh: %w", err) return yggdrasil.Token{}, fmt.Errorf("Refresh: %w", err)
} }
@ -257,7 +268,7 @@ func (y *Yggdrasil) BatchProfile(ctx context.Context, names []string) ([]yggdras
} }
func (y *Yggdrasil) PlayerCertificates(ctx context.Context, token string) (yggdrasil.Certificates, error) { func (y *Yggdrasil) PlayerCertificates(ctx context.Context, token string) (yggdrasil.Certificates, error) {
t, err := sutils.Auth(ctx, yggdrasil.ValidateToken{AccessToken: token}, y.client, &y.prikey.PublicKey, false) t, err := sutils.Auth(ctx, yggdrasil.ValidateToken{AccessToken: token}, y.client, y.cache, &y.prikey.PublicKey, false)
if err != nil { if err != nil {
return yggdrasil.Certificates{}, fmt.Errorf("PlayerCertificates: %w", err) return yggdrasil.Certificates{}, fmt.Errorf("PlayerCertificates: %w", err)
} }