From d54705c83f4a29af93e4ed3cdb4bc0038180032f Mon Sep 17 00:00:00 2001 From: Fleezesd <1253576349@qq.com> Date: Fri, 25 Aug 2023 11:39:39 +0800 Subject: [PATCH] jwt & error & log & auth middleware --- README.md | 5 +++- go.mod | 13 ++++++--- go.sum | 12 ++++++++ internal/iam/endpoint/endpoint.go | 21 ++++++++++++++ internal/iam/endpoint/middleware.go | 27 +++++++++++++++-- internal/iam/service/dto/request.go | 6 ++++ internal/iam/service/dto/response.go | 5 ++++ internal/iam/service/middleware.go | 12 ++++++-- internal/iam/service/model.go | 1 - internal/iam/service/service.go | 23 +++++++++++++++ internal/iam/transport/common.go | 17 ++++++++++- internal/iam/transport/transport.go | 11 ++++++- internal/pkg/errno/code.go | 29 +++++++++++++++++++ internal/pkg/errno/errno.go | 34 ++++++++++++++++++++++ internal/pkg/errno/user.go | 12 ++++++++ internal/pkg/log/log.go | 1 - pkg/auth/authn.go | 15 ++++++++++ pkg/token/token.go | 43 ++++++++++++++++++++++++++++ 18 files changed, 274 insertions(+), 13 deletions(-) create mode 100644 internal/iam/service/dto/request.go create mode 100644 internal/iam/service/dto/response.go delete mode 100644 internal/iam/service/model.go create mode 100644 internal/pkg/errno/code.go create mode 100644 internal/pkg/errno/errno.go create mode 100644 internal/pkg/errno/user.go create mode 100644 pkg/auth/authn.go create mode 100644 pkg/token/token.go diff --git a/README.md b/README.md index 5d434b0..ce25a47 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,12 @@ 微服务 go-kit study 后续会根据时间需求 用go-kratos 进行重构 # v1- kit base service & fx demo -建立 kit 基础服务 fx 依赖注入demo +建立 kit 基础服务, fx 依赖注入demo # v2- middleware & request uuid & log & project-layout 建立中间件 请求uuid log集成zap方便扩展 project-layout目录结构初步设定 +# v3- jwt & error & log & auth middleware +建立 jwt 认证, error 自定义错误码, log 封装zap, auth中间件 + diff --git a/go.mod b/go.mod index 7b771a3..8c4519f 100644 --- a/go.mod +++ b/go.mod @@ -3,14 +3,19 @@ module kit-study go 1.20 require ( - github.com/go-kit/kit v0.12.0 // indirect + github.com/go-kit/kit v0.12.0 + github.com/golang-jwt/jwt/v5 v5.0.0 + github.com/satori/go.uuid v1.2.0 + go.uber.org/fx v1.20.0 + go.uber.org/zap v1.23.0 + golang.org/x/crypto v0.0.0-20210915214749-c084706c2272 +) + +require ( github.com/go-kit/log v0.2.0 // indirect github.com/go-logfmt/logfmt v0.5.1 // indirect - github.com/satori/go.uuid v1.2.0 // indirect go.uber.org/atomic v1.9.0 // indirect go.uber.org/dig v1.17.0 // indirect - go.uber.org/fx v1.20.0 // indirect go.uber.org/multierr v1.7.0 // indirect - go.uber.org/zap v1.23.0 // indirect golang.org/x/sys v0.0.0-20220412211240-33da011f77ad // indirect ) diff --git a/go.sum b/go.sum index 7b87c88..960385a 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,6 @@ +github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= 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/go-kit/kit v0.12.0 h1:e4o3o3IsBfAKQh5Qbbiqyfu97Ku7jrO/JbohvztANh4= github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEaizzs= @@ -6,12 +8,17 @@ github.com/go-kit/log v0.2.0 h1:7i2K3eKTos3Vc0enKCfnVcgHh2olr/MyfboYq7cAcFw= github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE= +github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= @@ -19,12 +26,17 @@ go.uber.org/dig v1.17.0 h1:5Chju+tUvcC+N7N6EV08BJz41UZuO3BmHcN4A287ZLI= go.uber.org/dig v1.17.0/go.mod h1:rTxpf7l5I0eBTlE6/9RL+lDybC7WFwY2QH55ZSjy1mU= go.uber.org/fx v1.20.0 h1:ZMC/pnRvhsthOZh9MZjMq5U8Or3mA9zBSPaLnzs3ihQ= go.uber.org/fx v1.20.0/go.mod h1:qCUj0btiR3/JnanEr1TYEePfSw6o/4qYJscgvzQ5Ub0= +go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= go.uber.org/multierr v1.7.0 h1:zaiO/rmgFjbmCXdSYJWQcdvOCsthmdaHfr3Gm2Kx4Ec= go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY= go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY= +golang.org/x/crypto v0.0.0-20210915214749-c084706c2272 h1:3erb+vDS8lU1sxfDHF4/hhWyaXnhIaO+7RgL4fDZORA= +golang.org/x/crypto v0.0.0-20210915214749-c084706c2272/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/internal/iam/endpoint/endpoint.go b/internal/iam/endpoint/endpoint.go index dcda938..2a39dd1 100644 --- a/internal/iam/endpoint/endpoint.go +++ b/internal/iam/endpoint/endpoint.go @@ -2,6 +2,7 @@ package endpoint import ( "context" + "kit-study/internal/iam/service/dto" "kit-study/internal/iam/service" @@ -12,6 +13,7 @@ import ( type EndPointServer struct { HealthEndPoint endpoint.Endpoint + LoginEndPoint endpoint.Endpoint } func NewEndPointServer(svc service.Service) EndPointServer { @@ -20,8 +22,14 @@ func NewEndPointServer(svc service.Service) EndPointServer { healthEndPoint = MakeHealthEndPoint(svc) healthEndPoint = logMiddleware()(healthEndPoint) } + var loginEndPoint endpoint.Endpoint + { + loginEndPoint = MakeLoginEndPoint(svc) + loginEndPoint = logMiddleware()(loginEndPoint) + } return EndPointServer{ HealthEndPoint: healthEndPoint, + LoginEndPoint: loginEndPoint, } } @@ -41,3 +49,16 @@ func (s EndPointServer) Health(ctx context.Context, request interface{}) (res in res, _ = s.HealthEndPoint(ctx, request) return res } + +func MakeLoginEndPoint(s service.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (response interface{}, err error) { + req := request.(dto.LoginRequest) + return s.Login(ctx, req) + } +} + +func (s EndPointServer) Login(ctx context.Context, req dto.LoginRequest) (rsp dto.LoginResponse, err error) { + res, err := s.LoginEndPoint(ctx, req) + rsp = res.(dto.LoginResponse) + return +} diff --git a/internal/iam/endpoint/middleware.go b/internal/iam/endpoint/middleware.go index 8b9e831..119e254 100644 --- a/internal/iam/endpoint/middleware.go +++ b/internal/iam/endpoint/middleware.go @@ -3,10 +3,12 @@ package endpoint import ( "context" "fmt" - "kit-study/internal/pkg/log" + "kit-study/internal/pkg/errno" + "kit-study/pkg/token" "time" "kit-study/internal/iam/service" + "kit-study/internal/pkg/log" "github.com/go-kit/kit/endpoint" ) @@ -16,9 +18,30 @@ func logMiddleware() endpoint.Middleware { return func(ctx context.Context, request interface{}) (response interface{}, err error) { // 记录请求耗时 defer func(begin time.Time) { - log.Debugw(fmt.Sprint(ctx.Value(service.ContextReqUUid)), "调用endpoint层logMiddleware", "处理完请求", "耗时毫秒", time.Since(begin).Milliseconds()) + log.Debugw(fmt.Sprint(ctx.Value(service.ContextReqUUid)), "logMiddleware", "endpoint", "耗时毫秒", time.Since(begin).Milliseconds()) }(time.Now()) return next(ctx, request) } } } + +func AuthMiddleware() endpoint.Middleware { + return func(next endpoint.Endpoint) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (response interface{}, err error) { + jwtToken := fmt.Sprint(ctx.Value(token.JWT_CONTEXT_KEY)) + if jwtToken == "" { + log.Debugw(fmt.Sprint(ctx.Value(service.ContextReqUUid)), "authMiddleware", "endpoint", "error", errno.ErrTokenEmpty.Message) + return "", errno.ErrTokenEmpty + } + jwtInfo, err := token.ParseToken(jwtToken) + if err != nil { + log.Debugw(fmt.Sprint(ctx.Value(service.ContextReqUUid)), "authMiddleware", "endpoint", "error", errno.ErrTokenInvalid.Message) + } + if v, ok := jwtInfo["Name"]; ok { + ctx = context.WithValue(ctx, "name", v) + } + return next(ctx, request) + } + + } +} diff --git a/internal/iam/service/dto/request.go b/internal/iam/service/dto/request.go new file mode 100644 index 0000000..3c2684b --- /dev/null +++ b/internal/iam/service/dto/request.go @@ -0,0 +1,6 @@ +package dto + +type LoginRequest struct { + Username string `json:"username"` + Password string `json:"password"` +} diff --git a/internal/iam/service/dto/response.go b/internal/iam/service/dto/response.go new file mode 100644 index 0000000..b8ee750 --- /dev/null +++ b/internal/iam/service/dto/response.go @@ -0,0 +1,5 @@ +package dto + +type LoginResponse struct { + Token string `json:"token"` +} diff --git a/internal/iam/service/middleware.go b/internal/iam/service/middleware.go index 35c9b0d..3c3a6ea 100644 --- a/internal/iam/service/middleware.go +++ b/internal/iam/service/middleware.go @@ -3,11 +3,11 @@ package service import ( "context" "fmt" + "kit-study/internal/iam/service/dto" "kit-study/internal/pkg/log" ) // 抽象:对应 Service 安装中间件 (serivce加一层装饰) -// 1. 日志中间件 const ContextReqUUid = "req_uuid" @@ -28,7 +28,7 @@ func NewLogMiddlewareServer() NewMiddleware { func (l *logMiddleware) Health(ctx context.Context) (out string, err error) { // log 装饰 记录调用结果 defer func() { - log.Debugw(fmt.Sprint(ctx.Value(ContextReqUUid)), "调用service层logMiddleware", "service:health", "res", out) + log.Debugw(fmt.Sprint(ctx.Value(ContextReqUUid)), "logmiddleware", "service-health", "res", out) }() out, err = l.next.Health(ctx) if err != nil { @@ -36,3 +36,11 @@ func (l *logMiddleware) Health(ctx context.Context) (out string, err error) { } return out, nil } + +func (l *logMiddleware) Login(ctx context.Context, req dto.LoginRequest) (rsp dto.LoginResponse, err error) { + defer func() { + log.Debugw(fmt.Sprint(ctx.Value(ContextReqUUid)), "logmiddleware", "service-login", "res", rsp) + }() + rsp, err = l.next.Login(ctx, req) + return +} diff --git a/internal/iam/service/model.go b/internal/iam/service/model.go deleted file mode 100644 index 6d43c33..0000000 --- a/internal/iam/service/model.go +++ /dev/null @@ -1 +0,0 @@ -package service diff --git a/internal/iam/service/service.go b/internal/iam/service/service.go index 9652995..0461ba8 100644 --- a/internal/iam/service/service.go +++ b/internal/iam/service/service.go @@ -3,10 +3,16 @@ package service import ( "context" "fmt" + "kit-study/internal/iam/service/dto" + "kit-study/internal/pkg/errno" + "kit-study/internal/pkg/log" + "kit-study/pkg/auth" + "kit-study/pkg/token" ) type Service interface { Health(ctx context.Context) (string, error) + Login(ctx context.Context, req dto.LoginRequest) (rsp dto.LoginResponse, err error) } func NewService() Service { @@ -23,5 +29,22 @@ type baseServer struct { var _ Service = (*baseServer)(nil) func (s baseServer) Health(ctx context.Context) (string, error) { + log.Debugw(fmt.Sprint(ctx.Value(ContextReqUUid), "service", "health")) return fmt.Sprintln("health"), nil } + +func (s baseServer) Login(ctx context.Context, req dto.LoginRequest) (rsp dto.LoginResponse, err error) { + log.Debugw(fmt.Sprint(ctx.Value(ContextReqUUid)), "service", "login") + if req.Username != "admin" { + return rsp, errno.ErrUserNotFound + } + // bcrypt-hash加密 + if err := auth.Compare("$2a$10$RJrPY12wL8uTl.o7gdQtburp5Y9VxFODYiwhaBrQJxbC7jhEfYjbC", req.Password); err != nil { + return rsp, errno.ErrPasswordIncorrect + } + rsp.Token, err = token.Sign(req.Username) + if err != nil { + return rsp, errno.ErrSignToken + } + return +} diff --git a/internal/iam/transport/common.go b/internal/iam/transport/common.go index 204243b..a03b811 100644 --- a/internal/iam/transport/common.go +++ b/internal/iam/transport/common.go @@ -3,12 +3,16 @@ package transport import ( "context" "encoding/json" + "fmt" + "kit-study/internal/iam/service" + "kit-study/internal/iam/service/dto" + "kit-study/internal/pkg/log" "net/http" "github.com/go-kit/kit/endpoint" ) -// 解析请求HTPP & 数据转换 dto & 处理endpoint层的返回值 +// 解析请求HTTP & 数据转换 dto & 处理endpoint层的返回值 // 解析 HTTP 请求 func decodeHTTPHealthRequest(ctx context.Context, r *http.Request) (interface{}, error) { @@ -16,12 +20,23 @@ func decodeHTTPHealthRequest(ctx context.Context, r *http.Request) (interface{}, return nil, nil } +func decodeHTTPLoginRequest(ctx context.Context, r *http.Request) (interface{}, error) { + var login dto.LoginRequest + err := json.NewDecoder(r.Body).Decode(&login) // 从body体中拿取 + if err != nil { + return nil, err + } + log.Debugw(fmt.Sprint(ctx.Value(service.ContextReqUUid)), "解析请求数据", login) + return login, nil +} + // 处理返回数据 func encodeHTTPGenericResponse(ctx context.Context, w http.ResponseWriter, response interface{}) error { if f, ok := response.(endpoint.Failer); ok && f.Failed() != nil { errorEncoder(ctx, f.Failed(), w) return nil } + log.Debugw(fmt.Sprint(ctx.Value(service.ContextReqUUid)), "请求结束返回值", response) w.Header().Set("Content-Type", "application/json; charset=utf-8") return json.NewEncoder(w).Encode(response) } diff --git a/internal/iam/transport/transport.go b/internal/iam/transport/transport.go index 2d35d7d..efe51e4 100644 --- a/internal/iam/transport/transport.go +++ b/internal/iam/transport/transport.go @@ -2,6 +2,7 @@ package transport import ( "context" + "kit-study/pkg/token" "net/http" e "kit-study/internal/iam/endpoint" @@ -19,8 +20,10 @@ func NewHttpHandler(endpoint e.EndPointServer) http.Handler { httptransport.ServerErrorEncoder(errorEncoder), //程序中的全部报错都会走这里面 httptransport.ServerBefore(func(ctx context.Context, request *http.Request) context.Context { // 添加middleware 增加请求的uuid UUID := uuid.NewV5(uuid.NewV4(), "req_uuid").String() - log.Debugw("给请求添加uuid", "UUID", UUID) ctx = context.WithValue(ctx, service.ContextReqUUid, UUID) + log.Debugw("给请求添加uuid", "UUID", UUID) + ctx = context.WithValue(ctx, token.JWT_CONTEXT_KEY, request.Header.Get("Authorization")) + log.Debugw("把请求中的token发送到context中", "Token", request.Header.Get("Authorization")) return ctx }), } @@ -32,5 +35,11 @@ func NewHttpHandler(endpoint e.EndPointServer) http.Handler { encodeHTTPGenericResponse, //返回值 options..., )) + m.Handle("/login", httptransport.NewServer( + endpoint.LoginEndPoint, + decodeHTTPLoginRequest, + encodeHTTPGenericResponse, + options..., + )) return m } diff --git a/internal/pkg/errno/code.go b/internal/pkg/errno/code.go new file mode 100644 index 0000000..7513580 --- /dev/null +++ b/internal/pkg/errno/code.go @@ -0,0 +1,29 @@ +package errno + +var ( + // OK 代表请求成功. + OK = &Errno{HTTP: 200, Code: "", Message: ""} + + // InternalServerError 表示所有未知的服务器端错误. + InternalServerError = &Errno{HTTP: 500, Code: "InternalError", Message: "Internal server error."} + + // ErrPageNotFound 表示路由不匹配错误. + ErrPageNotFound = &Errno{HTTP: 404, Code: "ResourceNotFound.PageNotFound", Message: "Page not found."} + + // ErrBind 表示参数绑定错误. + ErrBind = &Errno{HTTP: 400, Code: "InvalidParameter.BindError", Message: "Error occurred while binding the request body to the struct."} + + // ErrInvalidParameter 表示所有验证失败的错误. + ErrInvalidParameter = &Errno{HTTP: 400, Code: "InvalidParameter", Message: "Parameter verification failed."} + + // ErrSignToken 表示签发 JWT Token 时出错. + ErrSignToken = &Errno{HTTP: 401, Code: "AuthFailure.SignTokenError", Message: "Error occurred while signing the JSON web token."} + + // ErrTokenInvalid 表示 JWT Token 格式错误. + ErrTokenInvalid = &Errno{HTTP: 401, Code: "AuthFailure.TokenInvalid", Message: "Token was invalid."} + + // ErrTokenEmpty 表示JWT Token 为空错误 + ErrTokenEmpty = &Errno{HTTP: 401, Code: "AuthFailure.TokenInvalid", Message: "Token was empty."} + // ErrUnauthorized 表示请求没有被授权. + ErrUnauthorized = &Errno{HTTP: 401, Code: "AuthFailure.Unauthorized", Message: "Unauthorized."} +) \ No newline at end of file diff --git a/internal/pkg/errno/errno.go b/internal/pkg/errno/errno.go new file mode 100644 index 0000000..e1a5ee8 --- /dev/null +++ b/internal/pkg/errno/errno.go @@ -0,0 +1,34 @@ +package errno + +import "fmt" + +type Errno struct { + HTTP int + Code string + Message string +} + +// Error 实现 error 接口中的 `Error` 方法. +func (err *Errno) Error() string { + return err.Message +} + +// SetMessage 设置 Errno 类型错误中的 Message 字段. +func (err *Errno) SetMessage(format string, args ...interface{}) *Errno { + err.Message = fmt.Sprintf(format, args...) + return err +} + +// Decode 尝试从 err 中解析出业务错误码和错误信息. +func Decode(err error) (int, string, string) { + if err == nil { + return OK.HTTP, OK.Code, OK.Message + } + + switch typed := err.(type) { + case *Errno: + return typed.HTTP, typed.Code, typed.Message + default: + return InternalServerError.HTTP, InternalServerError.Code, InternalServerError.Message + } +} diff --git a/internal/pkg/errno/user.go b/internal/pkg/errno/user.go new file mode 100644 index 0000000..1d3d84c --- /dev/null +++ b/internal/pkg/errno/user.go @@ -0,0 +1,12 @@ +package errno + +var ( + // ErrUserAlreadyExist 代表用户已经存在. + ErrUserAlreadyExist = &Errno{HTTP: 400, Code: "FailedOperation.UserAlreadyExist", Message: "User already exist."} + + // ErrUserNotFound 表示未找到用户. + ErrUserNotFound = &Errno{HTTP: 404, Code: "ResourceNotFound.UserNotFound", Message: "User was not found."} + + // ErrPasswordIncorrect 表示密码不正确. + ErrPasswordIncorrect = &Errno{HTTP: 401, Code: "InvalidParameter.PasswordIncorrect", Message: "Password was incorrect."} +) diff --git a/internal/pkg/log/log.go b/internal/pkg/log/log.go index 1874e35..6b372cf 100644 --- a/internal/pkg/log/log.go +++ b/internal/pkg/log/log.go @@ -1,4 +1,3 @@ -// Package log is a log package used by miniblog project. package log import ( diff --git a/pkg/auth/authn.go b/pkg/auth/authn.go new file mode 100644 index 0000000..a856e27 --- /dev/null +++ b/pkg/auth/authn.go @@ -0,0 +1,15 @@ +package auth + +import "golang.org/x/crypto/bcrypt" + +// Encrypt 使用 bcrypt 加密纯文本. +func Encrypt(source string) (string, error) { + hashedBytes, err := bcrypt.GenerateFromPassword([]byte(source), bcrypt.DefaultCost) + + return string(hashedBytes), err +} + +// Compare 比较密文和明文是否相同. +func Compare(hashedPassword, password string) error { + return bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password)) +} \ No newline at end of file diff --git a/pkg/token/token.go b/pkg/token/token.go new file mode 100644 index 0000000..ad706a3 --- /dev/null +++ b/pkg/token/token.go @@ -0,0 +1,43 @@ +package token + +import ( + "kit-study/internal/pkg/errno" + "time" + + "github.com/golang-jwt/jwt/v5" +) + +var jwtSecret = []byte("jwtSecret") + +const JWT_CONTEXT_KEY = "jwt_context_key" + +func ParseToken(token string) (jwt.MapClaims, error) { + jwtToken, err := jwt.ParseWithClaims(token, jwt.MapClaims{}, func(token *jwt.Token) (interface{}, error) { + return jwtSecret, nil + }) + if err != nil || jwtToken == nil { + return nil, errno.ErrTokenInvalid + } + claim, ok := jwtToken.Claims.(jwt.MapClaims) + if ok && jwtToken.Valid { + return claim, nil + } else { + return nil, nil + } +} + +// Sign 使用 jwtSecret 签发 token,token 的 claims 中会存放传入的 subject. +func Sign(secret string) (tokenString string, err error) { + token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ + JWT_CONTEXT_KEY: secret, // 身份标识 + "nbf": time.Now().Unix(), // 生效时间 + "iat": time.Now().Unix(), // 签发时间 + "exp": time.Now().Add(100000 * time.Hour).Unix(), // 过期时间 + "sub": "login", + }) + + // 签发 token + tokenString, err = token.SignedString(jwtSecret) + + return +}