feat/rest
李伟乐 1 year ago
parent 216d62b326
commit c7ec5eb105
  1. 24
      api/user_http.pb.go
  2. 2
      core/token/aes.go
  3. 48
      core/token/store.go
  4. 157
      core/token/token.go
  5. 24
      core/token/token_test.go
  6. 3
      example/main.go
  7. 18
      example/service/service.go
  8. 2
      go.mod
  9. 4
      go.sum
  10. 2
      middleware/jwt/ctx.go
  11. 56
      middleware/jwt/default.go
  12. 109
      middleware/jwt/jwt.go
  13. 16
      middleware/jwt/jwt_test.go
  14. 87
      middleware/jwt/token.go
  15. 23
      rest/httpx/requests.go

@ -1,11 +1,11 @@
package user package user
import ( import (
"context" "fmt"
"net/http" "git.diulo.com/mogfee/kit/middleware/jwt"
"git.diulo.com/mogfee/kit/rest" "git.diulo.com/mogfee/kit/rest"
"git.diulo.com/mogfee/kit/rest/httpx" "git.diulo.com/mogfee/kit/rest/httpx"
"git.diulo.com/mogfee/kit/middleware/jwt" "net/http"
) )
func RegisterUserHTTPServer(server *rest.Server, srv UserServer) { func RegisterUserHTTPServer(server *rest.Server, srv UserServer) {
@ -49,17 +49,21 @@ func UserListHandler(srv UserServer) http.HandlerFunc {
var ctx = r.Context() var ctx = r.Context()
ctx = jwt.SetAuthKeyContext(ctx, "user:list") ctx = jwt.SetAuthKeyContext(ctx, "user:list")
ctx = jwt.SetNeedAuthContext(ctx, true) 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 { if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(ctx, w, err) httpx.ErrorCtx(ctx, w, err)
return return
} }
resp, err := srv.List(ctx, &req) //resp, err := srv.List(ctx, &req)
if err != nil { //if err != nil {
httpx.ErrorCtx(ctx, w, err) // httpx.ErrorCtx(ctx, w, err)
} else { //} else {
httpx.OkJsonCtx(ctx, w, resp) httpx.OkJsonCtx(ctx, w, req)
} //}
} }
} }

@ -1,4 +1,4 @@
package jwt package token
import ( import (
"bytes" "bytes"

@ -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),
}
}

@ -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
}

@ -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))
}

@ -17,10 +17,11 @@ func main() {
}) })
srv.Use(func(next http.HandlerFunc) http.HandlerFunc { srv.Use(func(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
//tokenStr := `fS6HZv4HoMo+OnaNsLuM7O4Kx9L4UrM2TdnJB/J5qK75mJiEsuTyELYxaZXkFMnqjZp1lZ4V1vNATA4Tdhhfc/chcYQISAaWTmqjSoHROIMEGFg9x1+d/6MR6scX6g9JkX5beXKvgpeQhi2Q2SLjquBgWaPbaaPbM2owVYUICg4Z5aSNpd1n4dQfnRIkOCpIo7EFFy9ZRBD05xGs8/kPPBVe10ZEXpgcSBgKPkVVT1a0C0XZ2AwMfHU/dKf5gEJh`
next(w, r) next(w, r)
} }
}) })
user.RegisterUserHTTPServer(srv, service.NewUserService("abc")) user.RegisterUserHTTPServer(srv, service.NewUserService("sfe023f_9fd&fwfl"))
fmt.Println(srv.Start()) fmt.Println(srv.Start())

@ -3,6 +3,7 @@ package service
import ( import (
"context" "context"
user "git.diulo.com/mogfee/kit/api" user "git.diulo.com/mogfee/kit/api"
"git.diulo.com/mogfee/kit/core/token"
"git.diulo.com/mogfee/kit/middleware/jwt" "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) { 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", UserId: "1",
UserName: "test", UserName: "test",
UserType: "user", UserType: "user",
@ -55,31 +57,35 @@ func (s *userServer) LoginWithList(ctx context.Context, request *user.Request) (
if err != nil { if err != nil {
return nil, err 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) { func (s *userServer) Login(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", UserId: "1",
UserName: "test", UserName: "test",
UserType: "user", UserType: "user",
Permissions: []string{"user:list"},
UniqueId: "", UniqueId: "",
}) })
if err != nil { if err != nil {
return nil, err 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) { func (s *userServer) Login1(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", UserId: "1",
UserName: "test", UserName: "test",
UserType: "user", UserType: "user",
Permissions: []string{"user:list"},
UniqueId: "", UniqueId: "",
}) })
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &user.Response{Token: token}, nil return &user.Response{Token: tokenStr}, nil
} }

@ -39,6 +39,7 @@ require (
github.com/coreos/go-semver v0.3.0 // indirect github.com/coreos/go-semver v0.3.0 // indirect
github.com/coreos/go-systemd/v22 v22.3.2 // indirect github.com/coreos/go-systemd/v22 v22.3.2 // indirect
github.com/davecgh/go-spew v1.1.1 // 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/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-logr/logr v1.2.4 // 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/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect
github.com/prometheus/common v0.44.0 // indirect github.com/prometheus/common v0.44.0 // indirect
github.com/prometheus/procfs v0.11.1 // 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/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.11 // indirect github.com/ugorji/go/codec v1.2.11 // indirect
go.etcd.io/etcd/api/v3 v3.5.9 // indirect go.etcd.io/etcd/api/v3 v3.5.9 // indirect

@ -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.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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= 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/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 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI=
github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY= 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 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= 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= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=

@ -1,6 +1,6 @@
package jwt package jwt
import "golang.org/x/net/context" import "context"
type userIdKey struct{} type userIdKey struct{}
type authKey struct{} type authKey struct{}

@ -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", "权限不足")
}

@ -1,56 +1,28 @@
package jwt package jwt
import ( import (
"context" "git.diulo.com/mogfee/kit/core/token"
"git.diulo.com/mogfee/kit/errors" "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) type UserInfo struct {
UserId string
func WithJwtKey(jwtKey string) JwtOption { UserName string
return func(o *options) { UserType string
o.jwtKey = jwtKey Permissions []string
} UniqueId string
} }
func WithFromKey(fromKey string) JwtOption { func Middleware() rest.Middleware {
return func(o *options) { tokenServer := token.NewTokenService("sfe023f_9fd&fwfl")
o.fromKey = fromKey return func(next http.HandlerFunc) http.HandlerFunc {
} return func(w http.ResponseWriter, r *http.Request) {
} ctx := r.Context()
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
}
func JWT(opts ...JwtOption) middleware.Middleware {
var cfg = &options{
jwtKey: "JssLx22bjQwnyqby",
fromKey: "header:token",
//validate: &JwtDefault{},
}
for _, o := range opts {
o(cfg)
}
return func(handler middleware.Handler) middleware.Handler {
return func(ctx context.Context, a any) (any, error) {
//1. 解析token //1. 解析token
//2. 获取用户信息 //2. 获取用户信息
//3. 校验权限 //3. 校验权限
@ -59,36 +31,59 @@ func JWT(opts ...JwtOption) middleware.Middleware {
needAuth := FromNeedAuthContext(ctx) needAuth := FromNeedAuthContext(ctx)
//解析token //解析token
//tokenStr := cfg.validate.GetToken(ctx, cfg.fromKey) tokenStr := r.Header.Get("token")
//if tokenStr == "" && needAuth { if tokenStr == "" {
// return nil, errors.Unauthorized("NO_TOKEN", "") tokenStr = r.URL.Query().Get("token")
//} }
tokenStr := "" if tokenStr == "" && needAuth {
httpx.Error(w, errors.Unauthorized("NO_TOKEN", ""))
return
}
if tokenStr != "" { if tokenStr != "" {
if err := func() error { if err := func() error {
userInfo, err := cfg.validate.ParseToken(ctx, cfg.jwtKey, tokenStr) res, err := tokenServer.Parse(tokenStr)
if err != nil { if err != nil {
return err return err
} }
if needAuth && userInfo.UserId == "" { if needAuth && res == nil {
return errors.Unauthorized("TOKEN_BAD", "") return errors.Unauthorized("TOKEN_BAD", "")
} }
if authKey != "" { if authKey != "" {
if err = cfg.validate.Validate(ctx, authKey, userInfo.Permissions); err != nil { if err = defaultValidate(authKey, res); err != nil {
return err return err
} }
} }
if userInfo.UserId != "" { //if userInfo.UserId != "" {
ctx = SetUserContext(ctx, userInfo) // ctx = SetUserContext(ctx, userInfo)
} //}
return nil return nil
}(); err != nil { }(); err != nil {
if needAuth { if needAuth {
return nil, err httpx.Error(w, err)
return
}
}
}
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
} }
return handler(ctx, a) for _, v := range userInfo.Permissions {
if allowMap[v] {
return nil
} }
} }
return errors.Forbidden("TOKEN_PERMISSION_BAD", "权限不足")
} }

@ -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))
}

@ -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", "")
}

@ -12,7 +12,7 @@ import (
const ( const (
formKey = "form" formKey = "form"
jsonKey = "json" queryKey = "query"
pathKey = "path" pathKey = "path"
maxMemory = 32 << 20 // 32MB maxMemory = 32 << 20 // 32MB
maxBodyLen = 8 << 20 // 8MB maxBodyLen = 8 << 20 // 8MB
@ -23,7 +23,7 @@ const (
var ( var (
formUnmarshaler = mapping.NewUnmarshaler(formKey, mapping.WithStringValues()) formUnmarshaler = mapping.NewUnmarshaler(formKey, mapping.WithStringValues())
pathUnmarshaler = mapping.NewUnmarshaler(pathKey, mapping.WithStringValues()) pathUnmarshaler = mapping.NewUnmarshaler(pathKey, mapping.WithStringValues())
queryUnmarshaler = mapping.NewUnmarshaler(jsonKey, mapping.WithStringValues()) queryUnmarshaler = mapping.NewUnmarshaler(queryKey, mapping.WithStringValues())
validator atomic.Value validator atomic.Value
) )
@ -43,18 +43,22 @@ func Parse(r *http.Request, v any) error {
if err := ParseForm(r, v); err != nil { if err := ParseForm(r, v); err != nil {
fmt.Println("ParseForm") fmt.Println("ParseForm")
return err return err
} }
if err := ParseHeaders(r, v); err != nil { if err := ParseHeaders(r, v); err != nil {
fmt.Println("ParseHeaders") fmt.Println("ParseHeaders")
return err 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 { if err := ParseJsonBody(r, v); err != nil {
fmt.Println("ParseJsonBody") fmt.Println("ParseJsonBody", err)
return err return err
} }
fmt.Printf("ParseJsonBody====%+v\n", v)
if val := validator.Load(); val != nil { if val := validator.Load(); val != nil {
return val.(Validator).Validate(r, v) return val.(Validator).Validate(r, v)
@ -62,6 +66,14 @@ func Parse(r *http.Request, v any) error {
return nil 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. // ParseHeaders parses the headers request.
func ParseHeaders(r *http.Request, v any) error { 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. // ParseJsonBody parses the post request which contains json in body.
func ParseJsonBody(r *http.Request, v any) error { func ParseJsonBody(r *http.Request, v any) error {
fmt.Println("withJsonBody(r)", withJsonBody(r))
if withJsonBody(r) { if withJsonBody(r) {
reader := io.LimitReader(r.Body, maxBodyLen) reader := io.LimitReader(r.Body, maxBodyLen)
return mapping.UnmarshalJsonReader(reader, v) return mapping.UnmarshalJsonReader(reader, v)

Loading…
Cancel
Save