feat: http di
This commit is contained in:
parent
61c6bd1d10
commit
8ea6ead6cc
@ -1,10 +0,0 @@
|
|||||||
package consts
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Request struct {
|
|
||||||
Http *gin.Context
|
|
||||||
User *UserTokenInfo
|
|
||||||
}
|
|
@ -23,4 +23,8 @@ type UserTokenInfo struct {
|
|||||||
Groups []string `json:"groups"`
|
Groups []string `json:"groups"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
Token UserTokenInfo
|
||||||
|
}
|
||||||
|
|
||||||
const UserTokenInfoKey = "user.jwt"
|
const UserTokenInfoKey = "user.jwt"
|
||||||
|
@ -1,14 +1,22 @@
|
|||||||
package user
|
package user
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"framework_v2/internal/consts"
|
"framework_v2/internal/consts"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"net/http"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func CurrentUser(req *consts.Request) {
|
//func CurrentUser(req *consts.Request) {
|
||||||
req.Http.JSON(http.StatusOK, gin.H{
|
// req.Http.JSON(http.StatusOK, gin.H{
|
||||||
"req": req.User.Sub,
|
// "req": req.User.Sub,
|
||||||
})
|
// })
|
||||||
|
//
|
||||||
|
//}
|
||||||
|
|
||||||
|
func CurrentUser(c *gin.Context, user *consts.User) {
|
||||||
|
fmt.Println("CurrentUser", user)
|
||||||
|
c.JSON(200, gin.H{
|
||||||
|
"IP": c.ClientIP(),
|
||||||
|
"User": user.Token.Sub,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
@ -5,5 +5,5 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func InitApiRoutes() {
|
func InitApiRoutes() {
|
||||||
HandleRoute(GET, "/", user.CurrentUser, MiddlewareJWTAuth, MiddlewareJSONResponse)
|
GET("/", MiddlewareJSONResponse, user.CurrentUser)
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,14 @@
|
|||||||
package providers
|
package providers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"framework_v2/internal/access"
|
"framework_v2/internal/access"
|
||||||
"framework_v2/internal/consts"
|
"framework_v2/internal/consts"
|
||||||
|
"framework_v2/internal/helper"
|
||||||
ginzap "github.com/gin-contrib/zap"
|
ginzap "github.com/gin-contrib/zap"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"net/http"
|
||||||
|
"reflect"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -18,43 +22,101 @@ func InitGin() {
|
|||||||
//access.Router.Use(ginzap.RecoveryWithZap(access.Logger, true))
|
//access.Router.Use(ginzap.RecoveryWithZap(access.Logger, true))
|
||||||
}
|
}
|
||||||
|
|
||||||
type httpMethod int
|
//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)
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
//}
|
||||||
|
|
||||||
var httpMethodStr = []string{"GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"}
|
//func handleWithMetadata(c *gin.Context, controller HandlerFunc) {
|
||||||
|
// var metadata = &consts.Request{
|
||||||
|
// Http: c,
|
||||||
|
// User: GetAuthFromCtx(c),
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// controller(metadata)
|
||||||
|
//}
|
||||||
|
|
||||||
func (h httpMethod) String() string {
|
func GET(relativePath string, handlers ...interface{}) {
|
||||||
return httpMethodStr[h-1]
|
access.Router.GET(relativePath, func(c *gin.Context) {
|
||||||
}
|
doHandler(c, handlers...)
|
||||||
|
|
||||||
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) {
|
func POST(relativePath string, handlers ...interface{}) {
|
||||||
var metadata = &consts.Request{
|
access.Router.POST(relativePath, func(c *gin.Context) {
|
||||||
Http: c,
|
doHandler(c, handlers...)
|
||||||
User: GetAuthFromCtx(c),
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func PUT(relativePath string, handlers ...interface{}) {
|
||||||
|
access.Router.PUT(relativePath, func(c *gin.Context) {
|
||||||
|
doHandler(c, handlers...)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func PATCH(relativePath string, handlers ...interface{}) {
|
||||||
|
access.Router.PATCH(relativePath, func(c *gin.Context) {
|
||||||
|
doHandler(c, handlers...)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func DELETE(relativePath string, handlers ...interface{}) {
|
||||||
|
access.Router.DELETE(relativePath, func(c *gin.Context) {
|
||||||
|
doHandler(c, handlers...)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func doHandler(c *gin.Context, handlers ...interface{}) {
|
||||||
|
for _, handler := range handlers {
|
||||||
|
if c.IsAborted() {
|
||||||
|
// 是否已经响应
|
||||||
|
if c.Writer.Written() {
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
helper.ResponseError(c, http.StatusBadRequest, ErrEmptyResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
controller(metadata)
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
wrapHandler(c, handler)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func wrapHandler(c *gin.Context, f interface{}) {
|
||||||
|
fnValue := reflect.ValueOf(f)
|
||||||
|
fnType := fnValue.Type()
|
||||||
|
|
||||||
|
var args []reflect.Value
|
||||||
|
|
||||||
|
for i := 0; i < fnType.NumIn(); i++ {
|
||||||
|
argType := fnType.In(i)
|
||||||
|
|
||||||
|
var argValue reflect.Value
|
||||||
|
switch argType {
|
||||||
|
case reflect.TypeOf((*gin.Context)(nil)):
|
||||||
|
argValue = reflect.ValueOf(c)
|
||||||
|
case reflect.TypeOf((*consts.User)(nil)):
|
||||||
|
userInfo := DIJWTAuth(c)
|
||||||
|
if userInfo == nil {
|
||||||
|
helper.ResponseError(c, http.StatusUnauthorized, ErrNotValidToken)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
argValue = reflect.ValueOf(userInfo)
|
||||||
|
default:
|
||||||
|
helper.ResponseError(c, http.StatusBadRequest, fmt.Errorf("invalid argument type: %s", argType.String()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
args = append(args, argValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
fnValue.Call(args)
|
||||||
}
|
}
|
||||||
|
@ -14,58 +14,58 @@ var (
|
|||||||
ErrNotValidToken = errors.New("无效的 JWT 令牌。")
|
ErrNotValidToken = errors.New("无效的 JWT 令牌。")
|
||||||
ErrJWTFormatError = errors.New("JWT 格式错误。")
|
ErrJWTFormatError = errors.New("JWT 格式错误。")
|
||||||
ErrNotBearerType = errors.New("不是 Bearer 类型。")
|
ErrNotBearerType = errors.New("不是 Bearer 类型。")
|
||||||
|
ErrEmptyResponse = errors.New("我们的服务器返回了空请求,可能某些环节出了问题。")
|
||||||
)
|
)
|
||||||
|
|
||||||
const AnonymousUser = "anonymous"
|
const AnonymousUser = "anonymous"
|
||||||
|
|
||||||
func MiddlewareJWTAuth(c *gin.Context) {
|
// DIJWTAuth 用于注入到方法签名中。我觉得下面的代码以后可以优化。
|
||||||
|
func DIJWTAuth(c *gin.Context) *consts.User {
|
||||||
var sub = AnonymousUser
|
var sub = AnonymousUser
|
||||||
var jwtIdToken = &consts.UserTokenInfo{}
|
var jwtIdToken = &consts.User{}
|
||||||
|
|
||||||
if Config.DebugMode.Enable {
|
if Config.DebugMode.Enable {
|
||||||
jwtIdToken.Sub = sub
|
jwtIdToken.Token.Sub = sub
|
||||||
} else {
|
} else {
|
||||||
// get authorization header
|
// get authorization header
|
||||||
authorization := c.Request.Header.Get("Authorization")
|
authorization := c.Request.Header.Get("Authorization")
|
||||||
|
|
||||||
if authorization == "" {
|
if authorization == "" {
|
||||||
helper.ResponseError(c, http.StatusUnauthorized, ErrJWTFormatError)
|
helper.ResponseError(c, http.StatusUnauthorized, ErrJWTFormatError)
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
authSplit := strings.Split(authorization, " ")
|
authSplit := strings.Split(authorization, " ")
|
||||||
if len(authSplit) != 2 {
|
if len(authSplit) != 2 {
|
||||||
helper.ResponseError(c, http.StatusUnauthorized, ErrJWTFormatError)
|
helper.ResponseError(c, http.StatusUnauthorized, ErrJWTFormatError)
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if authSplit[0] != "Bearer" {
|
if authSplit[0] != "Bearer" {
|
||||||
helper.ResponseError(c, http.StatusUnauthorized, ErrNotBearerType)
|
helper.ResponseError(c, http.StatusUnauthorized, ErrNotBearerType)
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
token, err := ParseJWT(authSplit[1])
|
token, err := ParseJWT(authSplit[1])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
helper.ResponseError(c, http.StatusUnauthorized, ErrJWTFormatError)
|
helper.ResponseError(c, http.StatusUnauthorized, ErrJWTFormatError)
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
sub, err = token.Claims.GetSubject()
|
sub, err = token.Claims.GetSubject()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
helper.ResponseError(c, http.StatusUnauthorized, ErrNotValidToken)
|
helper.ResponseError(c, http.StatusUnauthorized, ErrNotValidToken)
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
err = mapstructure.Decode(token.Claims, &jwtIdToken)
|
err = mapstructure.Decode(token.Claims, &jwtIdToken.Token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Logger.Error("Failed to map token claims to JwtIDToken struct.\nError: " + err.Error())
|
Logger.Error("Failed to map token claims to JwtIDToken struct.\nError: " + err.Error())
|
||||||
helper.ResponseError(c, http.StatusUnauthorized, ErrNotValidToken)
|
helper.ResponseError(c, http.StatusUnauthorized, ErrNotValidToken)
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Set(consts.UserTokenInfoKey, jwtIdToken)
|
return jwtIdToken
|
||||||
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func MiddlewareJSONResponse(c *gin.Context) {
|
func MiddlewareJSONResponse(c *gin.Context) {
|
||||||
|
28
main_test.go
Normal file
28
main_test.go
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MyInterface interface{}
|
||||||
|
|
||||||
|
type MyStruct struct{}
|
||||||
|
|
||||||
|
func BenchmarkTypeAssertionSuccess(b *testing.B) {
|
||||||
|
var x MyInterface = &MyStruct{}
|
||||||
|
for n := 0; n < b.N; n++ {
|
||||||
|
if _, ok := x.(*MyStruct); !ok {
|
||||||
|
b.Fatal("Type assertion failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkTypeAssertionFailure(b *testing.B) {
|
||||||
|
var x MyInterface = &MyStruct{}
|
||||||
|
for n := 0; n < b.N; n++ {
|
||||||
|
if _, ok := x.(*gin.Context); ok {
|
||||||
|
b.Fatal("Type assertion should fail")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user