diff --git a/internal/access/http_context.go b/internal/access/http_context.go deleted file mode 100644 index 09bb592..0000000 --- a/internal/access/http_context.go +++ /dev/null @@ -1 +0,0 @@ -package access diff --git a/internal/consts/base_response.go b/internal/consts/base_response.go new file mode 100644 index 0000000..ff22fb6 --- /dev/null +++ b/internal/consts/base_response.go @@ -0,0 +1,8 @@ +package consts + +type BaseResponse struct { + Message string `json:"message"` + Error string `json:"error,omitempty"` + Code int `json:"code"` + Data any `json:"data,omitempty"` +} diff --git a/internal/consts/http_metadata.go b/internal/consts/http_metadata.go index cf1e8e3..8776f1a 100644 --- a/internal/consts/http_metadata.go +++ b/internal/consts/http_metadata.go @@ -4,7 +4,7 @@ import ( "github.com/gin-gonic/gin" ) -type Metadata struct { +type Request struct { Http *gin.Context User *UserTokenInfo } diff --git a/internal/consts/user.go b/internal/consts/user.go index f6fc680..da36228 100644 --- a/internal/consts/user.go +++ b/internal/consts/user.go @@ -22,3 +22,5 @@ type UserTokenInfo struct { Email string `json:"email"` Groups []string `json:"groups"` } + +const UserTokenInfoKey = "user.jwt" diff --git a/internal/helper/response.go b/internal/helper/response.go new file mode 100644 index 0000000..9293549 --- /dev/null +++ b/internal/helper/response.go @@ -0,0 +1,32 @@ +package helper + +import ( + "framework_v2/internal/consts" + "github.com/gin-gonic/gin" +) + +func ResponseMessage(c *gin.Context, code int, message string, data interface{}) { + c.JSON(code, &consts.BaseResponse{ + Message: message, + Code: code, + Data: data, + }) + c.Abort() +} + +func Response(c *gin.Context, code int, data interface{}) { + c.JSON(code, &consts.BaseResponse{ + Code: code, + Data: data, + }) + c.Abort() +} + +func ResponseError(c *gin.Context, code int, err error) { + c.JSON(code, &consts.BaseResponse{ + Error: err.Error(), + Code: code, + }) + c.Abort() + +} diff --git a/internal/http/controllers/user/main.go b/internal/http/controllers/user/main.go index 395a53b..8364133 100644 --- a/internal/http/controllers/user/main.go +++ b/internal/http/controllers/user/main.go @@ -6,8 +6,9 @@ import ( "net/http" ) -func CurrentUser(req *consts.Metadata) { +func CurrentUser(req *consts.Request) { req.Http.JSON(http.StatusOK, gin.H{ - "req": req.User, + "req": req.User.Sub, }) + } diff --git a/internal/http/http.go b/internal/http/http.go index 23932ef..2c3a5a7 100644 --- a/internal/http/http.go +++ b/internal/http/http.go @@ -2,7 +2,7 @@ package http import ( "framework_v2/internal/access" - "framework_v2/internal/http/routes" + "framework_v2/internal/providers" ) func InitHttp() { @@ -10,5 +10,5 @@ func InitHttp() { panic("You must call InitGin() before InitHttp()") } - routes.InitApiRoutes() + providers.InitApiRoutes() } diff --git a/internal/http/routes/api.go b/internal/http/routes/api.go deleted file mode 100644 index c967b38..0000000 --- a/internal/http/routes/api.go +++ /dev/null @@ -1,7 +0,0 @@ -package routes - -import "framework_v2/internal/http/controllers/user" - -func InitApiRoutes() { - HandleRoute(GET, "/", user.CurrentUser) -} diff --git a/internal/http/routes/routes.go b/internal/http/routes/routes.go deleted file mode 100644 index 8fb9094..0000000 --- a/internal/http/routes/routes.go +++ /dev/null @@ -1,45 +0,0 @@ -package routes - -import ( - "framework_v2/internal/access" - "framework_v2/internal/consts" - "github.com/gin-gonic/gin" -) - -type httpMethod int - -var httpMethodStr = []string{"GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"} - -func (h httpMethod) String() string { - return httpMethodStr[h-1] -} - -const ( - GET httpMethod = iota + 1 - POST - PUT - DELETE - PATCH - HEAD - OPTIONS -) - -type HandlerFunc func(metadata *consts.Metadata) - -func HandleRoute(method httpMethod, relativePath string, controller HandlerFunc, middlewares ...gin.HandlerFunc) { - access.Router.Handle(method.String(), relativePath, func(c *gin.Context) { - for _, middleware := range middlewares { - middleware(c) - } - - handleWithMetadata(c, controller) - }) -} - -func handleWithMetadata(c *gin.Context, controller HandlerFunc) { - var metadata = &consts.Metadata{ - Http: c, - } - - controller(metadata) -} diff --git a/internal/middlewares/.gitkeep b/internal/middlewares/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/internal/providers/api_routes.go b/internal/providers/api_routes.go new file mode 100644 index 0000000..37b0782 --- /dev/null +++ b/internal/providers/api_routes.go @@ -0,0 +1,9 @@ +package providers + +import ( + "framework_v2/internal/http/controllers/user" +) + +func InitApiRoutes() { + HandleRoute(GET, "/", user.CurrentUser, MiddlewareJWTAuth, MiddlewareJSONResponse) +} diff --git a/internal/providers/gin.go b/internal/providers/gin.go index 6c53d6f..bd348ed 100644 --- a/internal/providers/gin.go +++ b/internal/providers/gin.go @@ -2,6 +2,7 @@ package providers import ( "framework_v2/internal/access" + "framework_v2/internal/consts" ginzap "github.com/gin-contrib/zap" "github.com/gin-gonic/gin" "time" @@ -12,6 +13,48 @@ func InitGin() { access.Router = gin.New() - access.Router.Use(gin.Recovery(), ginzap.Ginzap(access.Logger, time.RFC3339, true)) - access.Router.Use(ginzap.RecoveryWithZap(access.Logger, true)) + access.Router.Use(ginzap.Ginzap(access.Logger, time.RFC3339, true)) + //access.Router.Use(gin.Recovery()) + //access.Router.Use(ginzap.RecoveryWithZap(access.Logger, true)) +} + +type httpMethod int + +var httpMethodStr = []string{"GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"} + +func (h httpMethod) String() string { + return httpMethodStr[h-1] +} + +const ( + GET httpMethod = iota + 1 + POST + PUT + DELETE + PATCH + HEAD + OPTIONS +) + +type HandlerFunc func(metadata *consts.Request) + +func HandleRoute(method httpMethod, relativePath string, controller HandlerFunc, middlewares ...gin.HandlerFunc) { + access.Router.Handle(method.String(), relativePath, func(c *gin.Context) { + for _, middleware := range middlewares { + middleware(c) + } + + if !c.IsAborted() { + handleWithMetadata(c, controller) + } + }) +} + +func handleWithMetadata(c *gin.Context, controller HandlerFunc) { + var metadata = &consts.Request{ + Http: c, + User: GetAuthFromCtx(c), + } + + controller(metadata) } diff --git a/internal/providers/gin_middleware.go b/internal/providers/gin_middleware.go new file mode 100644 index 0000000..949307d --- /dev/null +++ b/internal/providers/gin_middleware.go @@ -0,0 +1,77 @@ +package providers + +import ( + "errors" + "fmt" + "framework_v2/internal/consts" + "framework_v2/internal/helper" + "github.com/gin-gonic/gin" + "github.com/mitchellh/mapstructure" + "net/http" + "strings" +) + +var ( + ErrNotValidToken = errors.New("无效的 JWT 令牌。") + ErrJWTFormatError = errors.New("JWT 格式错误。") + ErrNotBearerType = errors.New("不是 Bearer 类型。") +) + +const AnonymousUser = "anonymous" + +func MiddlewareJWTAuth(c *gin.Context) { + var sub = AnonymousUser + var jwtIdToken = consts.UserTokenInfo{} + + if Config.DebugMode.Enable { + jwtIdToken.Sub = sub + } else { + // get authorization header + authorization := c.Request.Header.Get("Authorization") + + if authorization == "" { + helper.ResponseError(c, http.StatusUnauthorized, ErrJWTFormatError) + return + } + + authSplit := strings.Split(authorization, " ") + if len(authSplit) != 2 { + helper.ResponseError(c, http.StatusUnauthorized, ErrJWTFormatError) + return + } + + if authSplit[0] != "Bearer" { + helper.ResponseError(c, http.StatusUnauthorized, ErrNotBearerType) + return + } + + token, err := ParseJWT(authSplit[1]) + if err != nil { + helper.ResponseError(c, http.StatusUnauthorized, ErrJWTFormatError) + return + } + sub, err = token.Claims.GetSubject() + if err != nil { + helper.ResponseError(c, http.StatusUnauthorized, ErrNotValidToken) + return + } + + err = mapstructure.Decode(token.Claims, &jwtIdToken) + if err != nil { + Logger.Error("Failed to map token claims to JwtIDToken struct.\nError: " + err.Error()) + helper.ResponseError(c, http.StatusUnauthorized, ErrNotValidToken) + return + } + + fmt.Println("test", jwtIdToken.Sub) + } + + c.Set(consts.UserTokenInfoKey, jwtIdToken) + + return +} + +func MiddlewareJSONResponse(c *gin.Context) { + c.Header("Content-Type", "application/json; charset=utf-8") + c.Next() +}