From 2dd0a0002d1de0fcf12ed43b3e6c4f7194d77a91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E4=BC=9F=E4=B9=90?= Date: Tue, 14 Mar 2023 11:18:38 +0800 Subject: [PATCH] x --- Makefile | 2 +- api/user.http.go | 60 ++++++++++++ api/user.pb.go | 2 +- api/user_http.pb.go | 11 ++- cmd/gin-kit/main.go | 14 ++- cmd/kit/main.go | 9 +- constants/middleware.go | 3 - example/main.go | 37 ++++--- example/service/service.go | 5 + .../mogfee/kit/third_party/auth/auth.pb.go | 92 ++++++++++++++++++ go.mod | 2 + go.sum | 4 + internal/token/aes.go | 57 +++++++++++ internal/token/token.go | 85 ++++++++++++++++ internal/token/token_test.go | 18 ++++ internal/xuuid/xuuid.go | 14 +++ middleware/add_header.go | 1 - middleware/jwt.go | 28 ------ middleware/jwt/jwt.go | 96 +++++++++++++++++++ middleware/logging/logging.go | 1 + third_party/auth/auth.proto | 2 +- 21 files changed, 486 insertions(+), 57 deletions(-) create mode 100644 api/user.http.go create mode 100644 git.diulo.com/mogfee/kit/third_party/auth/auth.pb.go create mode 100644 internal/token/aes.go create mode 100644 internal/token/token.go create mode 100644 internal/token/token_test.go create mode 100644 internal/xuuid/xuuid.go delete mode 100644 middleware/jwt.go create mode 100644 middleware/jwt/jwt.go diff --git a/Makefile b/Makefile index 5c17bb4..3aeda1f 100644 --- a/Makefile +++ b/Makefile @@ -13,6 +13,6 @@ all: testts: make ts gen: - protoc -I ./third_party -I ./proto --ts_out=./api --go_out=./api --go-grpc_out=./api --kit_out=./api --validate_out="lang=go:./api" ./proto/*.proto + protoc -I ./third_party -I ./proto --ts_out=./api --go_out=./api --go-grpc_out=./api --kit_out=./api --gin-kit_out=./api --validate_out="lang=go:./api" ./proto/*.proto auth: protoc --go_out=. ./third_party/auth/auth.proto \ No newline at end of file diff --git a/api/user.http.go b/api/user.http.go new file mode 100644 index 0000000..34c6523 --- /dev/null +++ b/api/user.http.go @@ -0,0 +1,60 @@ +package user + +import ( + "git.diulo.com/mogfee/kit/errors" + "git.diulo.com/mogfee/kit/response" + "github.com/gin-gonic/gin" + "context" + "git.diulo.com/mogfee/kit/middleware" +) + +func RegisterUserHandler(app *gin.Engine, srv UserServer, m ...middleware.Middleware) { + app.GET("/api/v1/user/list", httpListHandler(srv, m...)) + app.POST("/api/v1/user/login", httpLoginHandler(srv, m...)) +} +func httpListHandler(srv UserServer, m ...middleware.Middleware) func(c *gin.Context) { + return func(c *gin.Context) { + var post LoginRequest + resp := response.New(c) + if err := resp.BindQuery(&post); err != nil { + resp.Error(err) + return + } + h := func(ctx context.Context, a any) (any, error) { + return srv.List(ctx, a.(*LoginRequest)) + } + out, err := middleware.HttpMiddleware(c, h, m...)(c, &post) + if err != nil { + resp.Error(err) + } else { + if v, ok := out.(*LoginResponse); ok { + resp.Success(v) + } else { + resp.Error(errors.InternalServer("RESULT_TYPE_ERROR", "LoginResponse")) + } + } + } +} +func httpLoginHandler(srv UserServer, m ...middleware.Middleware) func(c *gin.Context) { + return func(c *gin.Context) { + var post LoginRequest + resp := response.New(c) + if err := resp.BindJSON(&post); err != nil { + resp.Error(err) + return + } + h := func(ctx context.Context, a any) (any, error) { + return srv.Login(ctx, a.(*LoginRequest)) + } + out, err := middleware.HttpMiddleware(c, h, m...)(c, &post) + if err != nil { + resp.Error(err) + } else { + if v, ok := out.(*LoginResponse); ok { + resp.Success(v) + } else { + resp.Error(errors.InternalServer("RESULT_TYPE_ERROR", "LoginResponse")) + } + } + } +} diff --git a/api/user.pb.go b/api/user.pb.go index 7411258..356c804 100644 --- a/api/user.pb.go +++ b/api/user.pb.go @@ -7,6 +7,7 @@ package user import ( + _ "git.diulo.com/mogfee/kit/third_party/auth" _ "github.com/envoyproxy/protoc-gen-validate/validate" _ "google.golang.org/genproto/googleapis/api/annotations" protoreflect "google.golang.org/protobuf/reflect/protoreflect" @@ -14,7 +15,6 @@ import ( _ "google.golang.org/protobuf/types/descriptorpb" reflect "reflect" sync "sync" - _ "third_party/auth" ) const ( diff --git a/api/user_http.pb.go b/api/user_http.pb.go index 06dfcb8..6e9c5eb 100644 --- a/api/user_http.pb.go +++ b/api/user_http.pb.go @@ -1,8 +1,9 @@ package user import ( - "context" "git.diulo.com/mogfee/kit/transport/http" + "context" + "git.diulo.com/mogfee/kit/middleware/jwt" ) type UserHTTPServer interface { @@ -20,6 +21,8 @@ func _User_List0_HTTP_Handler(srv UserHTTPServer) func(ctx http.Context) error { return func(ctx http.Context) error { var in LoginRequest //user:search + var newCtx context.Context = ctx + newCtx = jwt.SetAuthKeyContext(ctx, "user:search") if err := ctx.BindQuery(&in); err != nil { return err } @@ -27,8 +30,7 @@ func _User_List0_HTTP_Handler(srv UserHTTPServer) func(ctx http.Context) error { h := ctx.Middleware(func(ctx context.Context, req interface{}) (interface{}, error) { return srv.List(ctx, req.(*LoginRequest)) }) - - out, err := h(ctx, &in) + out, err := h(newCtx, &in) if err != nil { return err } @@ -39,6 +41,7 @@ func _User_List0_HTTP_Handler(srv UserHTTPServer) func(ctx http.Context) error { func _User_Login0_HTTP_Handler(srv UserHTTPServer) func(ctx http.Context) error { return func(ctx http.Context) error { var in LoginRequest + var newCtx context.Context = ctx if err := ctx.Bind(&in); err != nil { return err } @@ -46,7 +49,7 @@ func _User_Login0_HTTP_Handler(srv UserHTTPServer) func(ctx http.Context) error h := ctx.Middleware(func(ctx context.Context, req interface{}) (interface{}, error) { return srv.Login(ctx, req.(*LoginRequest)) }) - out, err := h(ctx, &in) + out, err := h(newCtx, &in) if err != nil { return err } diff --git a/cmd/gin-kit/main.go b/cmd/gin-kit/main.go index 4a7ad64..7d74add 100644 --- a/cmd/gin-kit/main.go +++ b/cmd/gin-kit/main.go @@ -1,8 +1,11 @@ package main import ( + "fmt" protogen2 "git.diulo.com/mogfee/kit/protogen" "google.golang.org/protobuf/compiler/protogen" + "google.golang.org/protobuf/types/descriptorpb" + "strings" ) func main() { @@ -19,6 +22,15 @@ type Kit struct { func (u *Kit) addImports(imp string) { u.imports[imp] = imp } + +func (u *Kit) packageName(f *protogen.File) string { + a := f.Desc.Options().(*descriptorpb.FileOptions) + p := strings.Split(*a.GoPackage, ";") + if len(p) == 2 { + return p[1] + } + return fmt.Sprintf("%s", f.Desc.Name()) +} func (u *Kit) Generate(plugin *protogen.Plugin) error { if len(plugin.Files) < 1 { return nil @@ -34,7 +46,7 @@ func (u *Kit) Generate(plugin *protogen.Plugin) error { } fname := f.GeneratedFilenamePrefix + ".http.go" t := plugin.NewGeneratedFile(fname, f.GoImportPath) - t.P("package " + f.Desc.Name()) + t.P("package ", u.packageName(f)) t.P("import (") for _, v := range u.imports { t.P(`"` + v + `"`) diff --git a/cmd/kit/main.go b/cmd/kit/main.go index 48c230d..89592f9 100644 --- a/cmd/kit/main.go +++ b/cmd/kit/main.go @@ -35,6 +35,7 @@ func (u *Kit) Generate(plugin *protogen.Plugin) error { return nil } u.addImports("context") + u.addImports("git.diulo.com/mogfee/kit/middleware/jwt") u.addImports("git.diulo.com/mogfee/kit/transport/http") for _, f := range plugin.Files { if len(f.Services) == 0 { @@ -84,6 +85,7 @@ func (u *Kit) Generate(plugin *protogen.Plugin) error { } func (u *Kit) genGet(f *protogen.File, s *protogen.Service, t *protogen.GeneratedFile, m *protogen.Method) { + autkKey := protogen2.GetAuthKey(m) method, path := protogen2.GetProtoMethod(m) if method == "" { return @@ -94,7 +96,10 @@ func (u *Kit) genGet(f *protogen.File, s *protogen.Service, t *protogen.Generate var in `, m.Input.GoIdent.GoName) t.P("//", protogen2.GetAuthKey(m)) - + t.P(`var newCtx context.Context=ctx`) + if autkKey != "" { + t.P(`newCtx = jwt.SetAuthKeyContext(ctx, "`, autkKey, `")`) + } if method == protogen2.METHOD_GET { t.P(`if err := ctx.BindQuery(&in); err != nil { return err @@ -113,7 +118,7 @@ func (u *Kit) genGet(f *protogen.File, s *protogen.Service, t *protogen.Generate h := ctx.Middleware(func(ctx context.Context, req interface{}) (interface{}, error) { return srv.`, m.GoName, `(ctx, req.(*`, m.Input.GoIdent.GoName, `)) }) - out, err := h(ctx, &in) + out, err := h(newCtx, &in) if err != nil { return err } diff --git a/constants/middleware.go b/constants/middleware.go index f4aee6f..1255fc8 100644 --- a/constants/middleware.go +++ b/constants/middleware.go @@ -1,4 +1 @@ package constants - -type UserIdKey struct { -} diff --git a/example/main.go b/example/main.go index 3f5afa1..d2b6e87 100644 --- a/example/main.go +++ b/example/main.go @@ -7,6 +7,7 @@ import ( "git.diulo.com/mogfee/kit" user "git.diulo.com/mogfee/kit/api" "git.diulo.com/mogfee/kit/example/service" + "git.diulo.com/mogfee/kit/middleware/jwt" "git.diulo.com/mogfee/kit/middleware/logging" "git.diulo.com/mogfee/kit/middleware/validate" "git.diulo.com/mogfee/kit/registry/etcd" @@ -29,23 +30,27 @@ func runApp(host string) { http.Middleware( logging.Server(), validate.Server(), + jwt.JWT(), ), ) - route := hs.Route("/") - route.GET("/", func(ctx http.Context) error { - in := UserAddRequest{Name: "tom"} - http.SetOperation(ctx, "/api/abc") - h := ctx.Middleware(func(ctx context.Context, a any) (any, error) { - return AddUser(ctx, a.(*UserAddRequest)) - }) - out, err := h(ctx, &in) - if err != nil { - return err - } - reply, _ := out.(*UserAddResponse) - reply.Id = host - return ctx.Result(200, reply) - }) + //route := hs.Route("/") + //route.GET("/", func(ctx http.Context) error { + // in := UserAddRequest{Name: "tom"} + // http.SetOperation(ctx, "/api/abc") + // h := ctx.Middleware(func(ctx context.Context, a any) (any, error) { + // return AddUser(ctx, a.(*UserAddRequest)) + // }) + // if tr, ok := transport.FromServerContext(ctx); ok { + // fmt.Println(tr.Operation()) + // } + // out, err := h(ctx, &in) + // if err != nil { + // return err + // } + // reply, _ := out.(*UserAddResponse) + // reply.Id = host + // return ctx.Result(200, reply) + //}) user.RegisterUserHTTPServer(hs, &service.UserService{}) client, err := clientv3.New(clientv3.Config{ @@ -73,6 +78,8 @@ type UserAddResponse struct { } func AddUser(ctx context.Context, request *UserAddRequest) (*UserAddResponse, error) { + fmt.Println(jwt.FromUserContext(ctx)) + fmt.Println(jwt.FromAuthKeyContext(ctx)) return &UserAddResponse{Id: request.Name}, nil //errors.New(500, "xx", "") } diff --git a/example/service/service.go b/example/service/service.go index d731cdf..89e2dcd 100644 --- a/example/service/service.go +++ b/example/service/service.go @@ -3,7 +3,9 @@ package service import ( "context" "encoding/json" + "fmt" user "git.diulo.com/mogfee/kit/api" + "git.diulo.com/mogfee/kit/middleware/jwt" "git.diulo.com/mogfee/kit/transport" ) @@ -16,6 +18,9 @@ func (*UserService) Login(ctx context.Context, req *user.LoginRequest) (*user.Lo return &user.LoginResponse{Token: "182131292"}, nil } func (*UserService) List(ctx context.Context, req *user.LoginRequest) (*user.LoginResponse, error) { + fmt.Println(jwt.FromUserContext(ctx)) + fmt.Println(jwt.FromAuthKeyContext(ctx)) + tr, ok := transport.FromServerContext(ctx) if ok { return &user.LoginResponse{Token: tr.Operation()}, nil diff --git a/git.diulo.com/mogfee/kit/third_party/auth/auth.pb.go b/git.diulo.com/mogfee/kit/third_party/auth/auth.pb.go new file mode 100644 index 0000000..1b5414c --- /dev/null +++ b/git.diulo.com/mogfee/kit/third_party/auth/auth.pb.go @@ -0,0 +1,92 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.1 +// protoc v3.17.3 +// source: third_party/auth/auth.proto + +package auth + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + descriptorpb "google.golang.org/protobuf/types/descriptorpb" + reflect "reflect" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +var file_third_party_auth_auth_proto_extTypes = []protoimpl.ExtensionInfo{ + { + ExtendedType: (*descriptorpb.MethodOptions)(nil), + ExtensionType: (*string)(nil), + Field: 1111, + Name: "auth.auth_key", + Tag: "bytes,1111,opt,name=auth_key", + Filename: "third_party/auth/auth.proto", + }, +} + +// Extension fields to descriptorpb.MethodOptions. +var ( + // optional string auth_key = 1111; + E_AuthKey = &file_third_party_auth_auth_proto_extTypes[0] +) + +var File_third_party_auth_auth_proto protoreflect.FileDescriptor + +var file_third_party_auth_auth_proto_rawDesc = []byte{ + 0x0a, 0x1b, 0x74, 0x68, 0x69, 0x72, 0x64, 0x5f, 0x70, 0x61, 0x72, 0x74, 0x79, 0x2f, 0x61, 0x75, + 0x74, 0x68, 0x2f, 0x61, 0x75, 0x74, 0x68, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x04, 0x61, + 0x75, 0x74, 0x68, 0x1a, 0x20, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x3a, 0x3a, 0x0a, 0x08, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x6b, 0x65, + 0x79, 0x12, 0x1e, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x18, 0xd7, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x75, 0x74, 0x68, 0x4b, 0x65, + 0x79, 0x42, 0x2b, 0x5a, 0x29, 0x67, 0x69, 0x74, 0x2e, 0x64, 0x69, 0x75, 0x6c, 0x6f, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x6d, 0x6f, 0x67, 0x66, 0x65, 0x65, 0x2f, 0x6b, 0x69, 0x74, 0x2f, 0x74, 0x68, + 0x69, 0x72, 0x64, 0x5f, 0x70, 0x61, 0x72, 0x74, 0x79, 0x2f, 0x61, 0x75, 0x74, 0x68, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var file_third_party_auth_auth_proto_goTypes = []interface{}{ + (*descriptorpb.MethodOptions)(nil), // 0: google.protobuf.MethodOptions +} +var file_third_party_auth_auth_proto_depIdxs = []int32{ + 0, // 0: auth.auth_key:extendee -> google.protobuf.MethodOptions + 1, // [1:1] is the sub-list for method output_type + 1, // [1:1] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 0, // [0:1] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_third_party_auth_auth_proto_init() } +func file_third_party_auth_auth_proto_init() { + if File_third_party_auth_auth_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_third_party_auth_auth_proto_rawDesc, + NumEnums: 0, + NumMessages: 0, + NumExtensions: 1, + NumServices: 0, + }, + GoTypes: file_third_party_auth_auth_proto_goTypes, + DependencyIndexes: file_third_party_auth_auth_proto_depIdxs, + ExtensionInfos: file_third_party_auth_auth_proto_extTypes, + }.Build() + File_third_party_auth_auth_proto = out.File + file_third_party_auth_auth_proto_rawDesc = nil + file_third_party_auth_auth_proto_goTypes = nil + file_third_party_auth_auth_proto_depIdxs = nil +} diff --git a/go.mod b/go.mod index 6387d6d..f2b5836 100644 --- a/go.mod +++ b/go.mod @@ -18,12 +18,14 @@ require ( github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect github.com/coreos/go-semver v0.3.0 // indirect github.com/coreos/go-systemd/v22 v22.3.2 // indirect + github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.11.2 // indirect github.com/goccy/go-json v0.10.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang-jwt/jwt/v5 v5.0.0-rc.1 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/uuid v1.3.0 // indirect github.com/gorilla/mux v1.8.0 // indirect diff --git a/go.sum b/go.sum index 089fbc0..897359e 100644 --- a/go.sum +++ b/go.sum @@ -11,6 +11,8 @@ github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/envoyproxy/protoc-gen-validate v0.9.1 h1:PS7VIOgmSVhWUEeZwTe7z7zouA22Cr590PzXKbZHOVY= github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= @@ -31,6 +33,8 @@ github.com/goccy/go-json v0.10.0/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MG github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt/v5 v5.0.0-rc.1 h1:tDQ1LjKga657layZ4JLsRdxgvupebc0xuPwRNuTfUgs= +github.com/golang-jwt/jwt/v5 v5.0.0-rc.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= diff --git a/internal/token/aes.go b/internal/token/aes.go new file mode 100644 index 0000000..2fb53b7 --- /dev/null +++ b/internal/token/aes.go @@ -0,0 +1,57 @@ +package token + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "encoding/base64" +) + +// 加密 aes_128_cbc +func Encrypt(encryptStr string, key []byte, iv string) (string, error) { + encryptBytes := []byte(encryptStr) + block, err := aes.NewCipher(key) + if err != nil { + return "", err + } + + blockSize := block.BlockSize() + encryptBytes = pkcs5Padding(encryptBytes, blockSize) + + blockMode := cipher.NewCBCEncrypter(block, []byte(iv)) + encrypted := make([]byte, len(encryptBytes)) + blockMode.CryptBlocks(encrypted, encryptBytes) + return base64.URLEncoding.EncodeToString(encrypted), nil +} + +// 解密 +func Decrypt(decryptStr string, key []byte, iv string) (string, error) { + decryptBytes, err := base64.URLEncoding.DecodeString(decryptStr) + if err != nil { + return "", err + } + + block, err := aes.NewCipher(key) + if err != nil { + return "", err + } + + blockMode := cipher.NewCBCDecrypter(block, []byte(iv)) + decrypted := make([]byte, len(decryptBytes)) + + blockMode.CryptBlocks(decrypted, decryptBytes) + decrypted = pkcs5UnPadding(decrypted) + return string(decrypted), nil +} + +func pkcs5Padding(cipherText []byte, blockSize int) []byte { + padding := blockSize - len(cipherText)%blockSize + padText := bytes.Repeat([]byte{byte(padding)}, padding) + return append(cipherText, padText...) +} + +func pkcs5UnPadding(decrypted []byte) []byte { + length := len(decrypted) + unPadding := int(decrypted[length-1]) + return decrypted[:(length - unPadding)] +} diff --git a/internal/token/token.go b/internal/token/token.go new file mode 100644 index 0000000..df4fb20 --- /dev/null +++ b/internal/token/token.go @@ -0,0 +1,85 @@ +package token + +import ( + "encoding/json" + "git.diulo.com/mogfee/kit/errors" + "git.diulo.com/mogfee/kit/internal/xuuid" + "github.com/golang-jwt/jwt/v5" + "time" +) + +// iss : jwt签发者 +// sub : 主题名称 +// aud : 面向的用户,一般都是通过ip或者域名控制 +// exp : jwt的有效时间(过期时间),这个有效时间必须要大于签发时间,对于交互接口来说,建议是预设5秒 +// nbf : 在什么时候jwt开始生效(在此之前不可用) +// iat : jwt的签发时间 +// jti : 唯一标识,主要用来回避被重复使用攻击 + +type UserInfo struct { + UserId int64 + UserName string + UserType string + Permissions []string + UniqueId string +} + +func GetToken(key string, info *UserInfo) (token string, uniqId string, err error) { + uniqId = xuuid.UUID() + info.UniqueId = uniqId + var tokenStr string + ctime := time.Now().Unix() + tokenStr, err = jwt.NewWithClaims(jwt.SigningMethodHS256, &jwt.MapClaims{ + //jwt签发者 + //"iss": "auth.diulo.com", + //主题名称 + //"sub": "www.diulo.com", + // 面向的用户,一般都是通过ip或者域名控制 + //"aud": "api.diulo.com", + //jwt的有效时间(过期时间),这个有效时间必须要大于签发时间,对于交互接口来说,建议是预设5秒 + "exp": ctime + 7200, + //在什么时候jwt开始生效(在此之前不可用) + "nbf": ctime, + //jwt的签发时间 + "iat": ctime, + //唯一标识,主要用来回避被重复使用攻击 + "jti": uniqId, + "info": info, + }).SignedString([]byte(key)) + if err != nil { + return + } + token, err = Encrypt(tokenStr, []byte(key), key) + return +} + +func Parse(key string, tokenStr string) (*UserInfo, error) { + if tokenStr == "" { + return nil, errors.Unauthorized("TOKEN_ERROR", "") + } + str, err := Decrypt(tokenStr, []byte(key), key) + if err != nil { + return nil, err + } + token, err := jwt.Parse(str, func(token *jwt.Token) (interface{}, error) { + return []byte(key), nil + }) + if err != nil { + return nil, err + } + + if token.Valid { + row := struct { + Info *UserInfo + }{} + b, _ := json.Marshal(token.Claims) + if err = json.Unmarshal(b, &row); err != nil { + return nil, err + } + + return row.Info, nil + } else if errors.Is(err, jwt.ErrTokenExpired) || errors.Is(err, jwt.ErrTokenNotValidYet) { + return nil, errors.Unauthorized("TOKEN_EXPIRED", "") + } + return nil, errors.Unauthorized("TOKEN_ERROR", "") +} diff --git a/internal/token/token_test.go b/internal/token/token_test.go new file mode 100644 index 0000000..a36b691 --- /dev/null +++ b/internal/token/token_test.go @@ -0,0 +1,18 @@ +package token + +import ( + "fmt" + "testing" +) + +func TestGetToken(t *testing.T) { + tokenKey := "JssLx22bjQwnyqby" + token, uniqId, err := GetToken(tokenKey, &UserInfo{ + UserId: 111, + UserType: "admin", + Permissions: []string{"user:search"}, + }) + fmt.Println(token) + fmt.Println(uniqId, err) + fmt.Println(Parse(tokenKey, token)) +} diff --git a/internal/xuuid/xuuid.go b/internal/xuuid/xuuid.go new file mode 100644 index 0000000..2e535a0 --- /dev/null +++ b/internal/xuuid/xuuid.go @@ -0,0 +1,14 @@ +package xuuid + +import ( + "github.com/google/uuid" + "strings" +) + +func UUID() string { + u, err := uuid.NewUUID() + if err != nil { + return "" + } + return strings.Join(strings.Split(u.String(), "-"), "") +} diff --git a/middleware/add_header.go b/middleware/add_header.go index 6967956..bf9e539 100644 --- a/middleware/add_header.go +++ b/middleware/add_header.go @@ -17,7 +17,6 @@ func AddHeaderMdMiddle(c *gin.Context) Middleware { headers["remote_ip"] = []string{c.RemoteIP()} headers["full_path"] = []string{c.FullPath()} ctx = metadata.NewIncomingContext(ctx, headers) - return handler(ctx, a) } } diff --git a/middleware/jwt.go b/middleware/jwt.go deleted file mode 100644 index 2c98aae..0000000 --- a/middleware/jwt.go +++ /dev/null @@ -1,28 +0,0 @@ -package middleware - -import ( - "context" - "git.diulo.com/mogfee/kit/constants" - "google.golang.org/grpc/metadata" -) - -type ValidateUser interface { - ValidateUser(string) (int64, error) -} - -func JWT(validate ValidateUser) Middleware { - return func(handler Handler) Handler { - return func(ctx context.Context, a any) (any, error) { - var token string - if md, ok := metadata.FromIncomingContext(ctx); ok { - token = md.Get("token")[0] - } - userId, err := validate.ValidateUser(ctx.Value(token).(string)) - if err != nil { - return nil, err - } - ctx = context.WithValue(ctx, constants.UserIdKey{}, userId) - return handler(ctx, a) - } - } -} diff --git a/middleware/jwt/jwt.go b/middleware/jwt/jwt.go new file mode 100644 index 0000000..c2c62e8 --- /dev/null +++ b/middleware/jwt/jwt.go @@ -0,0 +1,96 @@ +package jwt + +import ( + "context" + "git.diulo.com/mogfee/kit/errors" + "git.diulo.com/mogfee/kit/internal/token" + "git.diulo.com/mogfee/kit/middleware" + "git.diulo.com/mogfee/kit/transport" +) + +type userIdKey struct{} +type authKey struct { +} +type JwtOption func(o *options) + +func WithJwtKey(jwtKey string) JwtOption { + return func(o *options) { + o.jwtKey = jwtKey + } +} + +func WithValidate(validate func(authKey string) error) JwtOption { + return func(o *options) { + o.validate = validate + } +} + +func WithValidatePermission(validatePermission func(permissions []string, key string) bool) JwtOption { + return func(o *options) { + o.validatePermission = validatePermission + } +} + +type options struct { + jwtKey string + validate func(authKey string) error + validatePermission func(validatePermission []string, key string) bool +} + +func JWT(opts ...JwtOption) middleware.Middleware { + var cfg = &options{ + jwtKey: "JssLx22bjQwnyqby", + validatePermission: InSlice, + } + for _, o := range opts { + o(cfg) + } + + return func(handler middleware.Handler) middleware.Handler { + return func(ctx context.Context, a any) (any, error) { + var tokenStr string + if tr, ok := transport.FromServerContext(ctx); ok { + tokenStr = tr.RequestHeader().Get("token") + } + userInfo, err := token.Parse(cfg.jwtKey, tokenStr) + if err != nil { + return nil, err + } + permission := FromAuthKeyContext(ctx) + if permission != "" { + if !cfg.validatePermission(userInfo.Permissions, permission) { + return nil, errors.Unauthorized("TOKEN_PERMISSION_BAD", "") + } + } + if cfg.validate != nil { + if err = cfg.validate(userInfo.UniqueId); err != nil { + return nil, err + } + } + ctx = SetUserContext(ctx, userInfo) + return handler(ctx, a) + } + } +} +func InSlice(validatePermission []string, key string) bool { + for _, e := range validatePermission { + if e == key { + return true + } + } + return false +} +func SetUserContext(ctx context.Context, user *token.UserInfo) context.Context { + return context.WithValue(ctx, userIdKey{}, user) +} +func FromUserContext(ctx context.Context) (user *token.UserInfo, ok bool) { + user, ok = ctx.Value(userIdKey{}).(*token.UserInfo) + return +} + +func SetAuthKeyContext(ctx context.Context, key string) context.Context { + return context.WithValue(ctx, authKey{}, key) +} +func FromAuthKeyContext(ctx context.Context) string { + return ctx.Value(authKey{}).(string) +} diff --git a/middleware/logging/logging.go b/middleware/logging/logging.go index 711b7a8..30bf5ad 100644 --- a/middleware/logging/logging.go +++ b/middleware/logging/logging.go @@ -8,6 +8,7 @@ import ( func Server() middleware.Middleware { return func(handler middleware.Handler) middleware.Handler { return func(ctx context.Context, a any) (any, error) { + ctx = context.WithValue(ctx, "user-id", "1111") return handler(ctx, a) } } diff --git a/third_party/auth/auth.proto b/third_party/auth/auth.proto index 3595d23..b4cdef9 100644 --- a/third_party/auth/auth.proto +++ b/third_party/auth/auth.proto @@ -1,6 +1,6 @@ syntax = "proto3"; package auth; -option go_package = "third_party/auth;auth"; +option go_package = "git.diulo.com/mogfee/kit/third_party/auth"; import "google/protobuf/descriptor.proto";