From fc7f3234dc5d0bd2d20ec2628a1bc318297eb690 Mon Sep 17 00:00:00 2001 From: xmdhs Date: Sun, 8 Oct 2023 15:32:21 +0800 Subject: [PATCH] =?UTF-8?q?=E7=94=A8=E4=B8=AD=E9=97=B4=E4=BB=B6=E9=AA=8C?= =?UTF-8?q?=E8=AF=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- handle/user.go | 5 ---- handle/yggdrasil/session.go | 12 +++----- handle/yggdrasil/texture.go | 24 +++++++-------- handle/yggdrasil/user.go | 54 ++++++---------------------------- handle/yggdrasil/yggdrasil.go | 32 ++++++++++++++++++++ server/route/route.go | 25 +++++++++------- service/yggdrasil/session.go | 10 ++----- service/yggdrasil/texture.go | 19 ++++-------- service/yggdrasil/user.go | 29 ++++-------------- service/yggdrasil/yggdrasil.go | 11 +++++++ 10 files changed, 93 insertions(+), 128 deletions(-) diff --git a/handle/user.go b/handle/user.go index f7c6a51..43fda8c 100644 --- a/handle/user.go +++ b/handle/user.go @@ -7,7 +7,6 @@ import ( "github.com/xmdhs/authlib-skin/model" "github.com/xmdhs/authlib-skin/service" - utilsService "github.com/xmdhs/authlib-skin/service/utils" "github.com/xmdhs/authlib-skin/utils" ) @@ -60,10 +59,6 @@ func (h *Handel) UserInfo() http.HandlerFunc { t := ctx.Value(tokenKey).(*model.TokenClaims) u, err := h.webService.Info(ctx, t) if err != nil { - if errors.Is(err, utilsService.ErrTokenInvalid) { - h.handleError(ctx, w, "token 无效", model.ErrAuth, 401, slog.LevelDebug) - return - } h.handleError(ctx, w, err.Error(), model.ErrService, 500, slog.LevelWarn) return } diff --git a/handle/yggdrasil/session.go b/handle/yggdrasil/session.go index 08135e1..1501946 100644 --- a/handle/yggdrasil/session.go +++ b/handle/yggdrasil/session.go @@ -2,12 +2,11 @@ package yggdrasil import ( "encoding/json" - "errors" "net/http" "github.com/samber/lo" + "github.com/xmdhs/authlib-skin/model" "github.com/xmdhs/authlib-skin/model/yggdrasil" - sutils "github.com/xmdhs/authlib-skin/service/utils" "github.com/xmdhs/authlib-skin/utils" ) @@ -18,18 +17,15 @@ func (y *Yggdrasil) SessionJoin() http.HandlerFunc { if !has { return } + t := ctx.Value(tokenKey).(*model.TokenClaims) + ip, err := utils.GetIP(r) if err != nil { y.handleYgError(ctx, w, err) return } - err = y.yggdrasilService.SessionJoin(ctx, a, ip) + err = y.yggdrasilService.SessionJoin(ctx, a, t, ip) if err != nil { - if errors.Is(err, sutils.ErrTokenInvalid) { - y.logger.DebugContext(ctx, err.Error()) - handleYgError(ctx, w, yggdrasil.Error{ErrorMessage: "Invalid token.", Error: "ForbiddenOperationException"}, 403) - return - } y.handleYgError(ctx, w, err) return } diff --git a/handle/yggdrasil/texture.go b/handle/yggdrasil/texture.go index 75febe8..25f05f9 100644 --- a/handle/yggdrasil/texture.go +++ b/handle/yggdrasil/texture.go @@ -12,8 +12,10 @@ import ( "strings" "github.com/go-chi/chi/v5" + "github.com/xmdhs/authlib-skin/model" "github.com/xmdhs/authlib-skin/model/yggdrasil" - "github.com/xmdhs/authlib-skin/service/utils" + + yggdrasilS "github.com/xmdhs/authlib-skin/service/yggdrasil" ) func (y *Yggdrasil) getTokenbyAuthorization(ctx context.Context, w http.ResponseWriter, r *http.Request) string { @@ -51,10 +53,7 @@ func (y *Yggdrasil) PutTexture() http.HandlerFunc { if !ok { return } - token := y.getTokenbyAuthorization(ctx, w, r) - if token == "" { - return - } + t := ctx.Value(tokenKey).(*model.TokenClaims) model := r.FormValue("model") @@ -100,9 +99,9 @@ func (y *Yggdrasil) PutTexture() http.HandlerFunc { return } - err = y.yggdrasilService.PutTexture(ctx, token, skin, model, uuid, textureType) + err = y.yggdrasilService.PutTexture(ctx, t, skin, model, uuid, textureType) if err != nil { - if errors.Is(err, utils.ErrTokenInvalid) { + if errors.Is(err, yggdrasilS.ErrUUIDNotEq) { y.logger.DebugContext(ctx, err.Error()) w.WriteHeader(401) return @@ -120,7 +119,7 @@ func getUUIDbyParams(ctx context.Context, l *slog.Logger, w http.ResponseWriter) textureType := chi.URLParamFromCtx(ctx, "textureType") if uuid == "" { l.DebugContext(ctx, "路径中缺少参数 uuid") - handleYgError(ctx, w, yggdrasil.Error{ErrorMessage: "路径中缺少参数 uuid / textureType"}, 400) + handleYgError(ctx, w, yggdrasil.Error{ErrorMessage: "路径中缺少参数 uuid"}, 400) return "", "", false } if textureType != "skin" && textureType != "cape" { @@ -139,13 +138,10 @@ func (y *Yggdrasil) DelTexture() http.HandlerFunc { if !ok { return } - token := y.getTokenbyAuthorization(ctx, w, r) - if token == "" { - return - } - err := y.yggdrasilService.DelTexture(ctx, uuid, token, textureType) + t := ctx.Value(tokenKey).(*model.TokenClaims) + err := y.yggdrasilService.DelTexture(ctx, uuid, t, textureType) if err != nil { - if errors.Is(err, utils.ErrTokenInvalid) { + if errors.Is(err, yggdrasilS.ErrUUIDNotEq) { y.logger.DebugContext(ctx, err.Error()) w.WriteHeader(401) return diff --git a/handle/yggdrasil/user.go b/handle/yggdrasil/user.go index 1b405e9..30fddc7 100644 --- a/handle/yggdrasil/user.go +++ b/handle/yggdrasil/user.go @@ -7,8 +7,8 @@ import ( "github.com/go-chi/chi/v5" "github.com/samber/lo" + "github.com/xmdhs/authlib-skin/model" "github.com/xmdhs/authlib-skin/model/yggdrasil" - sutils "github.com/xmdhs/authlib-skin/service/utils" yggdrasilS "github.com/xmdhs/authlib-skin/service/yggdrasil" ) @@ -36,21 +36,6 @@ func (y *Yggdrasil) Authenticate() http.HandlerFunc { func (y *Yggdrasil) Validate() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - cxt := r.Context() - a, has := getAnyModel[yggdrasil.ValidateToken](cxt, w, r.Body, y.validate, y.logger) - if !has { - return - } - err := y.yggdrasilService.ValidateToken(cxt, a) - if err != nil { - if errors.Is(err, sutils.ErrTokenInvalid) { - y.logger.DebugContext(cxt, err.Error()) - handleYgError(cxt, w, yggdrasil.Error{ErrorMessage: "Invalid token.", Error: "ForbiddenOperationException"}, 403) - return - } - y.handleYgError(cxt, w, err) - return - } w.WriteHeader(204) } } @@ -80,16 +65,11 @@ func (y *Yggdrasil) Invalidate() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(204) cxt := r.Context() - a, has := getAnyModel[yggdrasil.ValidateToken](cxt, w, r.Body, y.validate, y.logger) - if !has { - return - } - err := y.yggdrasilService.Invalidate(cxt, a.AccessToken) + + t := cxt.Value(tokenKey).(*model.TokenClaims) + + err := y.yggdrasilService.Invalidate(cxt, t) if err != nil { - if errors.Is(err, sutils.ErrTokenInvalid) { - y.logger.DebugContext(cxt, err.Error()) - return - } y.logger.WarnContext(cxt, err.Error()) } } @@ -98,17 +78,9 @@ func (y *Yggdrasil) Invalidate() http.HandlerFunc { func (y *Yggdrasil) Refresh() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { cxt := r.Context() - a, has := getAnyModel[yggdrasil.RefreshToken](cxt, w, r.Body, y.validate, y.logger) - if !has { - return - } - t, err := y.yggdrasilService.Refresh(cxt, a) + token := cxt.Value(tokenKey).(*model.TokenClaims) + t, err := y.yggdrasilService.Refresh(cxt, token) if err != nil { - if errors.Is(err, sutils.ErrTokenInvalid) { - y.logger.DebugContext(cxt, err.Error()) - handleYgError(cxt, w, yggdrasil.Error{ErrorMessage: "Invalid token.", Error: "ForbiddenOperationException"}, 403) - return - } y.handleYgError(cxt, w, err) return } @@ -176,17 +148,9 @@ func (y *Yggdrasil) BatchProfile() http.HandlerFunc { func (y *Yggdrasil) PlayerCertificates() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() - token := y.getTokenbyAuthorization(ctx, w, r) - if token == "" { - return - } - c, err := y.yggdrasilService.PlayerCertificates(ctx, token) + t := ctx.Value(tokenKey).(*model.TokenClaims) + c, err := y.yggdrasilService.PlayerCertificates(ctx, t) if err != nil { - if errors.Is(err, sutils.ErrTokenInvalid) { - y.logger.DebugContext(ctx, err.Error()) - handleYgError(ctx, w, yggdrasil.Error{ErrorMessage: "Invalid token.", Error: "ForbiddenOperationException"}, 403) - return - } y.handleYgError(ctx, w, err) return } diff --git a/handle/yggdrasil/yggdrasil.go b/handle/yggdrasil/yggdrasil.go index b428a7c..59e6776 100644 --- a/handle/yggdrasil/yggdrasil.go +++ b/handle/yggdrasil/yggdrasil.go @@ -3,6 +3,7 @@ package yggdrasil import ( "context" "encoding/json" + "errors" "io" "log/slog" "net" @@ -13,6 +14,7 @@ import ( "github.com/samber/lo" "github.com/xmdhs/authlib-skin/config" "github.com/xmdhs/authlib-skin/model/yggdrasil" + utilsS "github.com/xmdhs/authlib-skin/service/utils" yggdrasilS "github.com/xmdhs/authlib-skin/service/yggdrasil" "github.com/xmdhs/authlib-skin/utils" ) @@ -86,3 +88,33 @@ func (y *Yggdrasil) TextureAssets() http.HandlerFunc { http.StripPrefix("/texture/", http.FileServer(http.Dir(y.config.TexturePath))).ServeHTTP(w, r) } } + +type tokenValue string + +const tokenKey = tokenValue("token") + +func (y *Yggdrasil) Auth(handle http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + a, err := utils.DeCodeBody[yggdrasil.ValidateToken](r.Body, y.validate) + if err != nil { + token := y.getTokenbyAuthorization(ctx, w, r) + if token == "" { + return + } + a.AccessToken = token + } + t, err := y.yggdrasilService.Auth(ctx, a) + if err != nil { + if errors.Is(err, utilsS.ErrTokenInvalid) { + y.logger.DebugContext(ctx, err.Error()) + handleYgError(ctx, w, yggdrasil.Error{ErrorMessage: "Invalid token.", Error: "ForbiddenOperationException"}, 403) + return + } + y.handleYgError(ctx, w, err) + return + } + r = r.WithContext(context.WithValue(ctx, tokenKey, t)) + handle.ServeHTTP(w, r) + }) +} diff --git a/server/route/route.go b/server/route/route.go index 940c4ae..67c27cc 100644 --- a/server/route/route.go +++ b/server/route/route.go @@ -34,23 +34,28 @@ func newYggdrasil(handelY *yggdrasil.Yggdrasil) http.Handler { r := chi.NewRouter() r.Use(warpHJSON) - r.Post("/authserver/authenticate", handelY.Authenticate()) - r.Post("/authserver/validate", handelY.Validate()) - r.Post("/authserver/signout", handelY.Signout()) - r.Post("/authserver/invalidate", handelY.Invalidate()) - r.Post("/authserver/refresh", handelY.Refresh()) + r.Group(func(r chi.Router) { + r.Use(handelY.Auth) + r.Post("/authserver/validate", handelY.Validate()) + r.Post("/authserver/invalidate", handelY.Invalidate()) + r.Post("/authserver/refresh", handelY.Refresh()) - r.Put("/api/user/profile/{uuid}/{textureType}", handelY.PutTexture()) - r.Delete("/api/user/profile/{uuid}/{textureType}", handelY.DelTexture()) + r.Put("/api/user/profile/{uuid}/{textureType}", handelY.PutTexture()) + r.Delete("/api/user/profile/{uuid}/{textureType}", handelY.DelTexture()) + + r.Post("/sessionserver/session/minecraft/join", handelY.SessionJoin()) + r.Post("/minecraftservices/player/certificates", handelY.PlayerCertificates()) + + }) + + r.Post("/authserver/authenticate", handelY.Authenticate()) + r.Post("/authserver/signout", handelY.Signout()) r.Get("/sessionserver/session/minecraft/profile/{uuid}", handelY.GetProfile()) r.Post("/api/profiles/minecraft", handelY.BatchProfile()) - r.Post("/sessionserver/session/minecraft/join", handelY.SessionJoin()) r.Get("/sessionserver/session/minecraft/hasJoined", handelY.HasJoined()) - r.Post("/minecraftservices/player/certificates", handelY.PlayerCertificates()) - r.Get("/", handelY.YggdrasilRoot()) return r } diff --git a/service/yggdrasil/session.go b/service/yggdrasil/session.go index a596521..53a8bce 100644 --- a/service/yggdrasil/session.go +++ b/service/yggdrasil/session.go @@ -17,17 +17,11 @@ type sessionWithIP struct { IP string } -func (y *Yggdrasil) SessionJoin(ctx context.Context, s yggdrasil.Session, ip string) error { - t, err := sutils.Auth(ctx, yggdrasil.ValidateToken{ - AccessToken: s.AccessToken, - }, y.client, y.cache, &y.prikey.PublicKey, true) - if err != nil { - return fmt.Errorf("SessionJoin: %w", err) - } +func (y *Yggdrasil) SessionJoin(ctx context.Context, s yggdrasil.Session, t *model.TokenClaims, ip string) error { if s.SelectedProfile != t.Subject { return fmt.Errorf("SessionJoin: %w", sutils.ErrTokenInvalid) } - err = cache.CacheHelp[sessionWithIP]{Cache: y.cache}.Put([]byte("session"+s.ServerID), sessionWithIP{ + err := cache.CacheHelp[sessionWithIP]{Cache: y.cache}.Put([]byte("session"+s.ServerID), sessionWithIP{ User: *t, IP: ip, }, time.Now().Add(30*time.Second)) diff --git a/service/yggdrasil/texture.go b/service/yggdrasil/texture.go index 42fd6aa..ef5f4e7 100644 --- a/service/yggdrasil/texture.go +++ b/service/yggdrasil/texture.go @@ -15,8 +15,7 @@ import ( "github.com/xmdhs/authlib-skin/db/ent/user" "github.com/xmdhs/authlib-skin/db/ent/userprofile" "github.com/xmdhs/authlib-skin/db/ent/usertexture" - "github.com/xmdhs/authlib-skin/model/yggdrasil" - utilsService "github.com/xmdhs/authlib-skin/service/utils" + "github.com/xmdhs/authlib-skin/model" "github.com/xmdhs/authlib-skin/utils" ) @@ -78,13 +77,9 @@ func (y *Yggdrasil) delTexture(ctx context.Context, userProfileID int, textureTy return nil } -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.cache, &y.prikey.PublicKey, true) - if err != nil { - return fmt.Errorf("DelTexture: %w", err) - } +func (y *Yggdrasil) DelTexture(ctx context.Context, uuid string, t *model.TokenClaims, textureType string) error { if uuid != t.Subject { - return fmt.Errorf("PutTexture: %w", errors.Join(ErrUUIDNotEq, utilsService.ErrTokenInvalid)) + return fmt.Errorf("PutTexture: %w", ErrUUIDNotEq) } up, err := y.client.UserProfile.Query().Where(userprofile.HasUserWith(user.ID(t.UID))).First(ctx) if err != nil { @@ -101,13 +96,9 @@ func (y *Yggdrasil) DelTexture(ctx context.Context, uuid string, token string, t return nil } -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.cache, &y.prikey.PublicKey, true) - if err != nil { - return fmt.Errorf("PutTexture: %w", err) - } +func (y *Yggdrasil) PutTexture(ctx context.Context, t *model.TokenClaims, texturebyte []byte, model string, uuid string, textureType string) error { if uuid != t.Subject { - return fmt.Errorf("PutTexture: %w", errors.Join(ErrUUIDNotEq, utilsService.ErrTokenInvalid)) + return fmt.Errorf("PutTexture: %w", ErrUUIDNotEq) } up, err := y.client.UserProfile.Query().Where(userprofile.HasUserWith(user.ID(t.UID))).First(ctx) diff --git a/service/yggdrasil/user.go b/service/yggdrasil/user.go index 30f1eb4..e8f4598 100644 --- a/service/yggdrasil/user.go +++ b/service/yggdrasil/user.go @@ -23,6 +23,7 @@ import ( "github.com/xmdhs/authlib-skin/db/ent/user" "github.com/xmdhs/authlib-skin/db/ent/userprofile" "github.com/xmdhs/authlib-skin/db/ent/usertoken" + "github.com/xmdhs/authlib-skin/model" "github.com/xmdhs/authlib-skin/model/yggdrasil" sutils "github.com/xmdhs/authlib-skin/service/utils" "github.com/xmdhs/authlib-skin/utils" @@ -120,14 +121,6 @@ func (y *Yggdrasil) Authenticate(cxt context.Context, auth yggdrasil.Authenticat }, nil } -func (y *Yggdrasil) ValidateToken(ctx context.Context, t yggdrasil.ValidateToken) error { - _, err := sutils.Auth(ctx, t, y.client, y.cache, &y.prikey.PublicKey, true) - if err != nil { - return fmt.Errorf("ValidateToken: %w", err) - } - return nil -} - func (y *Yggdrasil) SignOut(ctx context.Context, t yggdrasil.Pass) error { u, err := y.validatePass(ctx, t.Username, t.Password) if err != nil { @@ -152,12 +145,8 @@ func (y *Yggdrasil) SignOut(ctx context.Context, t yggdrasil.Pass) error { return nil } -func (y *Yggdrasil) Invalidate(ctx context.Context, accessToken string) error { - t, err := sutils.Auth(ctx, yggdrasil.ValidateToken{AccessToken: accessToken}, y.client, y.cache, &y.prikey.PublicKey, true) - if err != nil { - return fmt.Errorf("Invalidate: %w", err) - } - err = y.client.UserToken.Update().Where(usertoken.HasUserWith(user.ID(t.UID))).AddTokenID(1).Exec(ctx) +func (y *Yggdrasil) Invalidate(ctx context.Context, t *model.TokenClaims) error { + err := y.client.UserToken.Update().Where(usertoken.HasUserWith(user.ID(t.UID))).AddTokenID(1).Exec(ctx) if err != nil { return fmt.Errorf("Invalidate: %w", err) } @@ -168,11 +157,7 @@ func (y *Yggdrasil) Invalidate(ctx context.Context, accessToken string) error { return nil } -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.cache, &y.prikey.PublicKey, false) - if err != nil { - return yggdrasil.Token{}, fmt.Errorf("Refresh: %w", err) - } +func (y *Yggdrasil) Refresh(ctx context.Context, t *model.TokenClaims) (yggdrasil.Token, error) { jwts, err := newJwtToken(y.prikey, t.Tid, t.CID, t.Subject, t.UID) if err != nil { return yggdrasil.Token{}, fmt.Errorf("Refresh: %w", err) @@ -315,11 +300,7 @@ func (y *Yggdrasil) BatchProfile(ctx context.Context, names []string) ([]yggdras // privateKey 为 PKCS #8, pem type 为 RSA PUBLIC KEY // 签名使用 rsaWIthsha1 -func (y *Yggdrasil) PlayerCertificates(ctx context.Context, token string) (yggdrasil.Certificates, error) { - t, err := sutils.Auth(ctx, yggdrasil.ValidateToken{AccessToken: token}, y.client, y.cache, &y.prikey.PublicKey, false) - if err != nil { - return yggdrasil.Certificates{}, fmt.Errorf("PlayerCertificates: %w", err) - } +func (y *Yggdrasil) PlayerCertificates(ctx context.Context, t *model.TokenClaims) (yggdrasil.Certificates, error) { rsa2048, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { return yggdrasil.Certificates{}, fmt.Errorf("PlayerCertificates: %w", err) diff --git a/service/yggdrasil/yggdrasil.go b/service/yggdrasil/yggdrasil.go index 9cff0ca..b364866 100644 --- a/service/yggdrasil/yggdrasil.go +++ b/service/yggdrasil/yggdrasil.go @@ -1,6 +1,7 @@ package yggdrasil import ( + "context" "crypto/rsa" "encoding/binary" "fmt" @@ -11,6 +12,8 @@ import ( "github.com/xmdhs/authlib-skin/db/cache" "github.com/xmdhs/authlib-skin/db/ent" "github.com/xmdhs/authlib-skin/model" + "github.com/xmdhs/authlib-skin/model/yggdrasil" + sutils "github.com/xmdhs/authlib-skin/service/utils" ) type Yggdrasil struct { @@ -82,3 +85,11 @@ func newJwtToken(jwtKey *rsa.PrivateKey, tokenID, clientToken, UUID string, user } return jwts, nil } + +func (y *Yggdrasil) Auth(ctx context.Context, t yggdrasil.ValidateToken) (*model.TokenClaims, error) { + u, err := sutils.Auth(ctx, t, y.client, y.cache, &y.prikey.PublicKey, true) + if err != nil { + return nil, fmt.Errorf("ValidateToken: %w", err) + } + return u, nil +}