Skip to content

Commit

Permalink
jwt & error & log & auth middleware
Browse files Browse the repository at this point in the history
  • Loading branch information
Fleezesd committed Aug 25, 2023
1 parent a05c01a commit d54705c
Show file tree
Hide file tree
Showing 18 changed files with 274 additions and 13 deletions.
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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中间件


13 changes: 9 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
12 changes: 12 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,30 +1,42 @@
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=
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=
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=
21 changes: 21 additions & 0 deletions internal/iam/endpoint/endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package endpoint

import (
"context"
"kit-study/internal/iam/service/dto"

"kit-study/internal/iam/service"

Expand All @@ -12,6 +13,7 @@ import (

type EndPointServer struct {
HealthEndPoint endpoint.Endpoint
LoginEndPoint endpoint.Endpoint
}

func NewEndPointServer(svc service.Service) EndPointServer {
Expand All @@ -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,
}
}

Expand All @@ -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
}
27 changes: 25 additions & 2 deletions internal/iam/endpoint/middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand All @@ -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)
}

}
}
6 changes: 6 additions & 0 deletions internal/iam/service/dto/request.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package dto

type LoginRequest struct {
Username string `json:"username"`
Password string `json:"password"`
}
5 changes: 5 additions & 0 deletions internal/iam/service/dto/response.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package dto

type LoginResponse struct {
Token string `json:"token"`
}
12 changes: 10 additions & 2 deletions internal/iam/service/middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand All @@ -28,11 +28,19 @@ 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 {
return "", err
}
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
}
1 change: 0 additions & 1 deletion internal/iam/service/model.go

This file was deleted.

23 changes: 23 additions & 0 deletions internal/iam/service/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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
}
17 changes: 16 additions & 1 deletion internal/iam/transport/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,40 @@ 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) {
// 此处可解析HTTP请求
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)
}
Expand Down
11 changes: 10 additions & 1 deletion internal/iam/transport/transport.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package transport

import (
"context"
"kit-study/pkg/token"
"net/http"

e "kit-study/internal/iam/endpoint"
Expand All @@ -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
}),
}
Expand All @@ -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
}
29 changes: 29 additions & 0 deletions internal/pkg/errno/code.go
Original file line number Diff line number Diff line change
@@ -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."}
)
Loading

0 comments on commit d54705c

Please sign in to comment.