diff --git a/api/user_http.pb.go b/api/user_http.pb.go index d0ede24..3d672b9 100644 --- a/api/user_http.pb.go +++ b/api/user_http.pb.go @@ -1,11 +1,11 @@ package user import ( - "context" - "net/http" + "fmt" + "git.diulo.com/mogfee/kit/middleware/jwt" "git.diulo.com/mogfee/kit/rest" "git.diulo.com/mogfee/kit/rest/httpx" - "git.diulo.com/mogfee/kit/middleware/jwt" + "net/http" ) func RegisterUserHTTPServer(server *rest.Server, srv UserServer) { @@ -49,17 +49,21 @@ func UserListHandler(srv UserServer) http.HandlerFunc { var ctx = r.Context() ctx = jwt.SetAuthKeyContext(ctx, "user:list") ctx = jwt.SetNeedAuthContext(ctx, true) - var req Request + var req struct { + Name string `query:"name"` + UserId int `query:"user_id"` + } + fmt.Println("xxx") if err := httpx.Parse(r, &req); err != nil { httpx.ErrorCtx(ctx, w, err) return } - resp, err := srv.List(ctx, &req) - if err != nil { - httpx.ErrorCtx(ctx, w, err) - } else { - httpx.OkJsonCtx(ctx, w, resp) - } + //resp, err := srv.List(ctx, &req) + //if err != nil { + // httpx.ErrorCtx(ctx, w, err) + //} else { + httpx.OkJsonCtx(ctx, w, req) + //} } } diff --git a/middleware/jwt/aes.go b/core/token/aes.go similarity index 99% rename from middleware/jwt/aes.go rename to core/token/aes.go index 7a8b6ab..2fb53b7 100644 --- a/middleware/jwt/aes.go +++ b/core/token/aes.go @@ -1,4 +1,4 @@ -package jwt +package token import ( "bytes" diff --git a/core/token/store.go b/core/token/store.go new file mode 100644 index 0000000..4aaf8f7 --- /dev/null +++ b/core/token/store.go @@ -0,0 +1,48 @@ +package token + +import ( + "sync" + "time" +) + +type ( + Store interface { + Set(key string, val any, duration time.Duration) error + Get(key string) (any, bool) + } + node struct { + data any + expired time.Time + } + defaultStore struct { + idMap map[string]*node + lock sync.RWMutex + } +) + +func (d *defaultStore) Get(key string) (any, bool) { + d.lock.RLock() + defer d.lock.RUnlock() + if v, ok := d.idMap[key]; ok { + if time.Now().After(v.expired) { + return v.data, true + } + } + return nil, false +} + +func (d *defaultStore) Set(key string, val any, duration time.Duration) error { + d.lock.Lock() + defer d.lock.Unlock() + d.idMap[key] = &node{ + data: val, + expired: time.Now().Add(duration), + } + return nil +} + +func newStore() Store { + return &defaultStore{ + idMap: make(map[string]*node), + } +} diff --git a/core/token/token.go b/core/token/token.go new file mode 100644 index 0000000..50eb4e6 --- /dev/null +++ b/core/token/token.go @@ -0,0 +1,157 @@ +package token + +import ( + "fmt" + "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 ( + Options func(opt *tokenServer) + + tokenServer struct { + prefix string + key string + expire time.Duration + generateKey func() string + store Store + } +) + +var ( + ErrTokenExpired = errors.Unauthorized("TOKEN_EXPIRED", "") + ErrTokenError = errors.Unauthorized("TOKEN_ERROR", "") +) + +func WithPrefix(prefix string) Options { + return func(opt *tokenServer) { + opt.prefix = prefix + } +} +func WithExpire(expire time.Duration) Options { + return func(opt *tokenServer) { + opt.expire = expire + } +} +func WithGenerateKey(fn func() string) Options { + return func(opt *tokenServer) { + opt.generateKey = fn + } +} +func WithStore(store Store) Options { + return func(opt *tokenServer) { + opt.store = store + } +} +func NewTokenService(key string, opts ...Options) *tokenServer { + srv := &tokenServer{ + key: key, + generateKey: func() string { + return xuuid.UUID() + }, + store: newStore(), + prefix: "token:", + } + for _, opt := range opts { + opt(srv) + } + return srv +} +func (t *tokenServer) getStoreKey(uniqId string) string { + return fmt.Sprintf("%s%s", t.prefix, uniqId) +} +func (t *tokenServer) Generate(info any) (string, error) { + token, uniqId, err := t.generate() + if err != nil { + return "", err + } + if err = t.store.Set(t.getStoreKey(uniqId), info, t.expire); err != nil { + return "", err + } + return token, nil +} + +func (t *tokenServer) generate() (token string, uniqId string, err error) { + uniqId = t.generateKey() + 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", + "aud": fmt.Sprint(uniqId), + //jwt的有效时间(过期时间),这个有效时间必须要大于签发时间,对于交互接口来说,建议是预设5秒 + //"exp": ctime + 7200, + //在什么时候jwt开始生效(在此之前不可用) + "nbf": ctime, + //jwt的签发时间 + "iat": ctime, + //唯一标识,主要用来回避被重复使用攻击 + //"jti": fmt.Sprint(uniqId), + }).SignedString([]byte(t.key)) + if err != nil { + return + } + token, err = Encrypt(tokenStr, []byte(t.key), t.key) + return +} + +func (t *tokenServer) Refresh(token string) error { + uniqId, info, err := t.parse(token) + if err != nil { + return err + } + return t.store.Set(uniqId, info, t.expire) +} +func (t *tokenServer) Parse(tokenStr string) (any, error) { + _, info, err := t.parse(tokenStr) + return info, err +} +func (t *tokenServer) parse(tokenStr string) (string, any, error) { + var res any + if tokenStr == "" { + return "", res, nil + } + str, err := Decrypt(tokenStr, []byte(t.key), t.key) + if err != nil { + return "", res, ErrTokenError + } + token, err := jwt.Parse(str, func(token *jwt.Token) (interface{}, error) { + return []byte(t.key), nil + }) + if err != nil { + if errors.Is(err, jwt.ErrTokenExpired) { + return "", res, ErrTokenExpired + } + return "", res, ErrTokenError + } + + if token.Valid { + audience, err := token.Claims.GetAudience() + if err != nil { + return "", res, ErrTokenError + } + if len(audience) > 0 { + if info, ok := t.store.Get(audience[0]); ok { + return audience[0], info, nil + } + } + return "", res, nil + } else if errors.Is(err, jwt.ErrTokenExpired) || errors.Is(err, jwt.ErrTokenNotValidYet) { + return "", res, ErrTokenExpired + } + return "", res, ErrTokenError +} diff --git a/core/token/token_test.go b/core/token/token_test.go new file mode 100644 index 0000000..1b27c89 --- /dev/null +++ b/core/token/token_test.go @@ -0,0 +1,24 @@ +package token + +import ( + "fmt" + "testing" +) + +func TestGenerateToken(t *testing.T) { + srv := NewTokenService("sfe023f_9fd&fwfl") + token, err := srv.Generate(map[string]any{ + "userId": 111, + }) + if err != nil { + t.Error(err) + return + } + + if err := srv.Refresh(token); err != nil { + t.Error(err) + return + } + fmt.Println(srv.Parse(token)) + +} diff --git a/example/main.go b/example/main.go index a303d25..ace240a 100644 --- a/example/main.go +++ b/example/main.go @@ -17,10 +17,11 @@ func main() { }) srv.Use(func(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { + //tokenStr := `fS6HZv4HoMo+OnaNsLuM7O4Kx9L4UrM2TdnJB/J5qK75mJiEsuTyELYxaZXkFMnqjZp1lZ4V1vNATA4Tdhhfc/chcYQISAaWTmqjSoHROIMEGFg9x1+d/6MR6scX6g9JkX5beXKvgpeQhi2Q2SLjquBgWaPbaaPbM2owVYUICg4Z5aSNpd1n4dQfnRIkOCpIo7EFFy9ZRBD05xGs8/kPPBVe10ZEXpgcSBgKPkVVT1a0C0XZ2AwMfHU/dKf5gEJh` next(w, r) } }) - user.RegisterUserHTTPServer(srv, service.NewUserService("abc")) + user.RegisterUserHTTPServer(srv, service.NewUserService("sfe023f_9fd&fwfl")) fmt.Println(srv.Start()) diff --git a/example/service/service.go b/example/service/service.go index 958a47e..769839e 100644 --- a/example/service/service.go +++ b/example/service/service.go @@ -3,6 +3,7 @@ package service import ( "context" user "git.diulo.com/mogfee/kit/api" + "git.diulo.com/mogfee/kit/core/token" "git.diulo.com/mogfee/kit/middleware/jwt" ) @@ -45,7 +46,8 @@ func (userServer) Auto(ctx context.Context, request *user.Request) (*user.Respon } func (s *userServer) LoginWithList(ctx context.Context, request *user.Request) (*user.Response, error) { - token, _, err := jwt.GetToken(s.tokenKey, &jwt.UserInfo{ + tokenService := token.NewTokenService(s.tokenKey) + tokenStr, err := tokenService.Generate(&jwt.UserInfo{ UserId: "1", UserName: "test", UserType: "user", @@ -55,31 +57,35 @@ func (s *userServer) LoginWithList(ctx context.Context, request *user.Request) ( if err != nil { return nil, err } - return &user.Response{Token: token}, nil + return &user.Response{Token: tokenStr}, nil } func (s *userServer) Login(ctx context.Context, request *user.Request) (*user.Response, error) { - token, _, err := jwt.GetToken(s.tokenKey, &jwt.UserInfo{ - UserId: "1", - UserName: "test", - UserType: "user", - UniqueId: "", + tokenService := token.NewTokenService(s.tokenKey) + tokenStr, err := tokenService.Generate(&jwt.UserInfo{ + UserId: "1", + UserName: "test", + UserType: "user", + Permissions: []string{"user:list"}, + UniqueId: "", }) if err != nil { return nil, err } - return &user.Response{Token: token}, nil + return &user.Response{Token: tokenStr}, nil } func (s *userServer) Login1(ctx context.Context, request *user.Request) (*user.Response, error) { - token, _, err := jwt.GetToken(s.tokenKey, &jwt.UserInfo{ - UserId: "1", - UserName: "test", - UserType: "user", - UniqueId: "", + tokenService := token.NewTokenService(s.tokenKey) + tokenStr, err := tokenService.Generate(&jwt.UserInfo{ + UserId: "1", + UserName: "test", + UserType: "user", + Permissions: []string{"user:list"}, + UniqueId: "", }) if err != nil { return nil, err } - return &user.Response{Token: token}, nil + return &user.Response{Token: tokenStr}, nil } diff --git a/go.mod b/go.mod index af6fb6f..444584b 100644 --- a/go.mod +++ b/go.mod @@ -39,6 +39,7 @@ require ( github.com/coreos/go-semver v0.3.0 // indirect github.com/coreos/go-systemd/v22 v22.3.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-logr/logr v1.2.4 // indirect @@ -62,6 +63,7 @@ require ( github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect github.com/prometheus/common v0.44.0 // indirect github.com/prometheus/procfs v0.11.1 // indirect + github.com/redis/go-redis/v9 v9.2.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.11 // indirect go.etcd.io/etcd/api/v3 v3.5.9 // indirect diff --git a/go.sum b/go.sum index 006698e..72c8e51 100644 --- a/go.sum +++ b/go.sum @@ -18,6 +18,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 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/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= @@ -105,6 +107,8 @@ github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdO github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI= github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY= +github.com/redis/go-redis/v9 v9.2.1 h1:WlYJg71ODF0dVspZZCpYmoF1+U1Jjk9Rwd7pq6QmlCg= +github.com/redis/go-redis/v9 v9.2.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= diff --git a/middleware/jwt/ctx.go b/middleware/jwt/ctx.go index a9f661e..90bc5f3 100644 --- a/middleware/jwt/ctx.go +++ b/middleware/jwt/ctx.go @@ -1,6 +1,6 @@ package jwt -import "golang.org/x/net/context" +import "context" type userIdKey struct{} type authKey struct{} diff --git a/middleware/jwt/default.go b/middleware/jwt/default.go deleted file mode 100644 index d774b2d..0000000 --- a/middleware/jwt/default.go +++ /dev/null @@ -1,56 +0,0 @@ -package jwt - -import ( - "context" - "git.diulo.com/mogfee/kit/errors" - "strings" -) - -type JwtDefault struct { -} - -//func (j *JwtDefault) GetToken(ctx context.Context, key string) (tokenStr string) { -// arr := strings.Split(key, ":") -// if len(arr) != 2 { -// return "" -// } -// switch arr[0] { -// case "cookie": -// if tr, ok := transport.FromServerContext(ctx); ok { -// if tr1, ok := tr.(http.Transporter); ok { -// if co, err := tr1.Request().Cookie(arr[1]); err == nil { -// return co.Value -// } -// } -// } -// case "header": -// if tr, ok := transport.FromServerContext(ctx); ok { -// return tr.RequestHeader().Get(arr[1]) -// } -// case "query": -// if tr, ok := transport.FromServerContext(ctx); ok { -// if ht, ok := tr.(http.Transporter); ok { -// return ht.Request().URL.Query().Get(arr[1]) -// } -// } -// } -// return "" -//} - -func (j *JwtDefault) ParseToken(ctx context.Context, key string, token string) (*UserInfo, error) { - return Parse(key, token) -} - -func (j *JwtDefault) Validate(ctx context.Context, permission string, permissions []string) error { - allowPers := strings.Split(permission, "|") - allowMap := make(map[string]bool, len(allowPers)) - for _, v := range allowPers { - allowMap[v] = true - } - for _, v := range permissions { - if allowMap[v] { - return nil - } - } - return errors.Forbidden("TOKEN_PERMISSION_BAD", "权限不足") -} diff --git a/middleware/jwt/jwt.go b/middleware/jwt/jwt.go index a88d048..c2680cb 100644 --- a/middleware/jwt/jwt.go +++ b/middleware/jwt/jwt.go @@ -1,56 +1,28 @@ package jwt import ( - "context" + "git.diulo.com/mogfee/kit/core/token" "git.diulo.com/mogfee/kit/errors" - "git.diulo.com/mogfee/kit/middleware" + "git.diulo.com/mogfee/kit/rest" + "git.diulo.com/mogfee/kit/rest/httpx" + "net/http" + "strings" ) -type JwtOption func(o *options) - -func WithJwtKey(jwtKey string) JwtOption { - return func(o *options) { - o.jwtKey = jwtKey - } -} - -func WithFromKey(fromKey string) JwtOption { - return func(o *options) { - o.fromKey = fromKey - } -} -func WithValidate(val JwtValidate) JwtOption { - return func(o *options) { - o.validate = val - } -} - -type options struct { - jwtKey string - fromKey string //cookie:token header:key - validate JwtValidate -} -type JwtValidate interface { - //GetToken 获取token - GetToken(ctx context.Context, key string) (tokenStr string) - //ParseToken 解析token获取用户信息 - ParseToken(ctx context.Context, key string, token string) (*UserInfo, error) - //Validate 校验权限 - Validate(ctx context.Context, permission string, permissions []string) error +type UserInfo struct { + UserId string + UserName string + UserType string + Permissions []string + UniqueId string } -func JWT(opts ...JwtOption) middleware.Middleware { - var cfg = &options{ - jwtKey: "JssLx22bjQwnyqby", - fromKey: "header:token", - //validate: &JwtDefault{}, - } - for _, o := range opts { - o(cfg) - } +func Middleware() rest.Middleware { + tokenServer := token.NewTokenService("sfe023f_9fd&fwfl") + return func(next http.HandlerFunc) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() - return func(handler middleware.Handler) middleware.Handler { - return func(ctx context.Context, a any) (any, error) { //1. 解析token //2. 获取用户信息 //3. 校验权限 @@ -58,37 +30,60 @@ func JWT(opts ...JwtOption) middleware.Middleware { authKey := FromAuthKeyContext(ctx) needAuth := FromNeedAuthContext(ctx) - // 解析token - //tokenStr := cfg.validate.GetToken(ctx, cfg.fromKey) - //if tokenStr == "" && needAuth { - // return nil, errors.Unauthorized("NO_TOKEN", "") - //} - tokenStr := "" + //解析token + tokenStr := r.Header.Get("token") + if tokenStr == "" { + tokenStr = r.URL.Query().Get("token") + } + if tokenStr == "" && needAuth { + httpx.Error(w, errors.Unauthorized("NO_TOKEN", "")) + return + } if tokenStr != "" { if err := func() error { - userInfo, err := cfg.validate.ParseToken(ctx, cfg.jwtKey, tokenStr) + res, err := tokenServer.Parse(tokenStr) if err != nil { return err } - if needAuth && userInfo.UserId == "" { + if needAuth && res == nil { return errors.Unauthorized("TOKEN_BAD", "") } if authKey != "" { - if err = cfg.validate.Validate(ctx, authKey, userInfo.Permissions); err != nil { + if err = defaultValidate(authKey, res); err != nil { return err } } - if userInfo.UserId != "" { - ctx = SetUserContext(ctx, userInfo) - } + //if userInfo.UserId != "" { + // ctx = SetUserContext(ctx, userInfo) + //} return nil }(); err != nil { if needAuth { - return nil, err + httpx.Error(w, err) + return } } } - return handler(ctx, a) + + next(w, r) + } + } +} + +func defaultValidate(authKey string, res any) error { + + userInfo, ok := res.(*UserInfo) + if !ok { + return errors.Forbidden("TOKEN_PERMISSION_BAD", "权限不足") + } + allowMap := make(map[string]bool) + for _, v := range strings.Split(authKey, "|") { + allowMap[v] = true + } + for _, v := range userInfo.Permissions { + if allowMap[v] { + return nil } } + return errors.Forbidden("TOKEN_PERMISSION_BAD", "权限不足") } diff --git a/middleware/jwt/jwt_test.go b/middleware/jwt/jwt_test.go deleted file mode 100644 index fb5cdb6..0000000 --- a/middleware/jwt/jwt_test.go +++ /dev/null @@ -1,16 +0,0 @@ -package jwt - -import ( - "context" - "fmt" - "strings" - "testing" -) - -func TestA(t *testing.T) { - str := `fS6HZv4HoMo+OnaNsLuM7O4Kx9L4UrM2TdnJB/J5qK75mJiEsuTyELYxaZXkFMnqre4A1B/pzzpFNKwB4k2M2tBcrSAakYU4I+cOFRcy7ANJdjis529x8Du89Mh16ZAViCHNVs+Rp6qHFK/hjdLVEkFY7Ws2t++cu4rF+DQacs9yccoh2wTCVweNOIrGz0fOaEgVroprhP4xvvfVUj293ovCv9T+mF9qHJYmswEMOu1+UMLLf3EyBVXgxnNrHzvX` - str = strings.ReplaceAll(str, " ", "+") - - tt := &JwtDefault{} - fmt.Println(tt.ParseToken(context.Background(), "sfe023f_9fd&fwfl", str)) -} diff --git a/middleware/jwt/token.go b/middleware/jwt/token.go deleted file mode 100644 index cf50924..0000000 --- a/middleware/jwt/token.go +++ /dev/null @@ -1,87 +0,0 @@ -package jwt - -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 string - 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 &UserInfo{}, nil - } - str, err := Decrypt(tokenStr, []byte(key), key) - if err != nil { - return nil, errors.Unauthorized("TOKEN_ERROR", err.Error()) - } - token, err := jwt.Parse(str, func(token *jwt.Token) (interface{}, error) { - return []byte(key), nil - }) - if err != nil { - if errors.Is(err, jwt.ErrTokenExpired) { - return nil, errors.Unauthorized("TOKEN_EXPIRED", "") - } - return nil, errors.Unauthorized("TOKEN_ERROR", err.Error()) - } - - if token.Valid { - row := struct { - Info *UserInfo - }{} - b, _ := json.Marshal(token.Claims) - if err = json.Unmarshal(b, &row); err != nil { - return nil, errors.Unauthorized("TOKEN_ERROR", err.Error()) - } - 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/rest/httpx/requests.go b/rest/httpx/requests.go index 646f313..9784938 100644 --- a/rest/httpx/requests.go +++ b/rest/httpx/requests.go @@ -12,7 +12,7 @@ import ( const ( formKey = "form" - jsonKey = "json" + queryKey = "query" pathKey = "path" maxMemory = 32 << 20 // 32MB maxBodyLen = 8 << 20 // 8MB @@ -23,7 +23,7 @@ const ( var ( formUnmarshaler = mapping.NewUnmarshaler(formKey, mapping.WithStringValues()) pathUnmarshaler = mapping.NewUnmarshaler(pathKey, mapping.WithStringValues()) - queryUnmarshaler = mapping.NewUnmarshaler(jsonKey, mapping.WithStringValues()) + queryUnmarshaler = mapping.NewUnmarshaler(queryKey, mapping.WithStringValues()) validator atomic.Value ) @@ -43,18 +43,22 @@ func Parse(r *http.Request, v any) error { if err := ParseForm(r, v); err != nil { fmt.Println("ParseForm") return err - } if err := ParseHeaders(r, v); err != nil { fmt.Println("ParseHeaders") return err } - + if err := ParseQuery(r, v); err != nil { + fmt.Println("ParseQuery") + return err + } + fmt.Printf("ParseQuery====%+v\n", v) if err := ParseJsonBody(r, v); err != nil { - fmt.Println("ParseJsonBody") + fmt.Println("ParseJsonBody", err) return err } + fmt.Printf("ParseJsonBody====%+v\n", v) if val := validator.Load(); val != nil { return val.(Validator).Validate(r, v) @@ -62,6 +66,14 @@ func Parse(r *http.Request, v any) error { return nil } +func ParseQuery(r *http.Request, v any) error { + params := make(map[string]any) + for k, v := range r.URL.Query() { + params[k] = v[0] + } + fmt.Printf("ParseQuery:%+v\n", params) + return queryUnmarshaler.Unmarshal(params, v) +} // ParseHeaders parses the headers request. func ParseHeaders(r *http.Request, v any) error { @@ -102,7 +114,6 @@ func ParseHeader(headerValue string) map[string]string { // ParseJsonBody parses the post request which contains json in body. func ParseJsonBody(r *http.Request, v any) error { - fmt.Println("withJsonBody(r)", withJsonBody(r)) if withJsonBody(r) { reader := io.LimitReader(r.Body, maxBodyLen) return mapping.UnmarshalJsonReader(reader, v)