From fd64b81b88d4b9cc58b9f3cd35db9dd7d10e1791 Mon Sep 17 00:00:00 2001 From: lwnmengjing Date: Mon, 14 Aug 2023 01:12:19 +0800 Subject: [PATCH 1/3] :sparkles: feat: support mss-boot-admin-api --- pkg/config/gormdb/casbin.go | 24 --------- pkg/config/gormdb/gorm.go | 2 +- pkg/config/gormdb/logger/logger.go | 10 +++- pkg/middlewares/auth.go | 83 ++++++++++++++++++++++++++++++ pkg/response/actions/get_gorm.go | 2 +- pkg/response/actions/get_mgm.go | 4 +- pkg/response/model.go | 4 +- pkg/response/return.go | 10 ++-- pkg/security/security.go | 2 +- pkg/store/oauth2.go | 32 ++++++++++++ 10 files changed, 135 insertions(+), 38 deletions(-) delete mode 100644 pkg/config/gormdb/casbin.go create mode 100644 pkg/middlewares/auth.go create mode 100644 pkg/store/oauth2.go diff --git a/pkg/config/gormdb/casbin.go b/pkg/config/gormdb/casbin.go deleted file mode 100644 index 43c70e4..0000000 --- a/pkg/config/gormdb/casbin.go +++ /dev/null @@ -1,24 +0,0 @@ -package gormdb - -/* - * @Author: lwnmengjing - * @Date: 2023/3/4 00:24:48 - * @Last Modified by: lwnmengjing - * @Last Modified time: 2023/3/4 00:24:48 - */ - -// CasbinRule casbin rule -type CasbinRule struct { - PType string `json:"pType" gorm:"size:100;"` - V0 string `json:"v0" gorm:"size:100;"` - V1 string `json:"v1" gorm:"size:100;"` - V2 string `json:"v2" gorm:"size:100;"` - V3 string `json:"v3" gorm:"size:100;"` - V4 string `json:"v4" gorm:"size:100;"` - V5 string `json:"v5" gorm:"size:100;"` -} - -// TableName table name -func (*CasbinRule) TableName() string { - return "casbin_rule" -} diff --git a/pkg/config/gormdb/gorm.go b/pkg/config/gormdb/gorm.go index d69d340..991a15a 100644 --- a/pkg/config/gormdb/gorm.go +++ b/pkg/config/gormdb/gorm.go @@ -95,7 +95,7 @@ func (e *Database) Init() { if e.CasbinModel != "" { //set casbin adapter var a persist.Adapter - a, err = gormadapter.NewAdapterByDBWithCustomTable(DB, &CasbinRule{}) + a, err = gormadapter.NewAdapterByDB(DB) if err != nil { log.Fatalf("gormadapter.NewAdapterByDB error : %s", err.Error()) } diff --git a/pkg/config/gormdb/logger/logger.go b/pkg/config/gormdb/logger/logger.go index 39d1e96..2d96e54 100644 --- a/pkg/config/gormdb/logger/logger.go +++ b/pkg/config/gormdb/logger/logger.go @@ -11,6 +11,12 @@ import ( "gorm.io/gorm/utils" ) +var log loggerCore.Logger + +func init() { + log = loggerCore.NewLogger() +} + // Colors const ( Reset = "\033[0m" @@ -36,11 +42,11 @@ type gormLogger struct { func (l *gormLogger) getLogger(ctx context.Context) loggerCore.Logger { requestID := ctx.Value("X-Request-ID") if requestID != nil { - return loggerCore.DefaultLogger.Fields(map[string]interface{}{ + return log.Fields(map[string]interface{}{ "x-request-id": requestID, }) } - return loggerCore.DefaultLogger + return log } // LogMode log mode diff --git a/pkg/middlewares/auth.go b/pkg/middlewares/auth.go new file mode 100644 index 0000000..5cd3dfd --- /dev/null +++ b/pkg/middlewares/auth.go @@ -0,0 +1,83 @@ +package middlewares + +/* + * @Author: lwnmengjing + * @Date: 2022/4/13 22:44 + * @Last Modified by: lwnmengjing + * @Last Modified time: 2022/4/13 22:44 + */ + +import ( + "errors" + "net/http" + "strings" + + "github.com/coreos/go-oidc/v3/oidc" + "github.com/gin-gonic/gin" + + "github.com/mss-boot-io/mss-boot/pkg/response" + "github.com/mss-boot-io/mss-boot/pkg/store" +) + +// AuthMiddleware 认证中间件 +func AuthMiddleware() gin.HandlerFunc { + return func(c *gin.Context) { + api := response.Make(c) + //登录认证 + accessToken := getTokenFromHeader(c) + if accessToken == "" { + api.AddError(errors.New("token is empty")) + api.Err(http.StatusUnauthorized) + return + } + client, err := store.DefaultOAuth2Store. + GetClientByDomain(c.Request.Context(), c.Request.Host) + if err != nil { + api.AddError(err) + api.Err(http.StatusUnauthorized) + return + } + provider, err := oidc.NewProvider(c, client.GetIssuer()) + if err != nil { + api.AddError(err) + api.Err(http.StatusUnauthorized) + return + } + idTokenVerifier := provider.Verifier(&oidc.Config{ClientID: client.GetClientID()}) + idToken, err := idTokenVerifier.Verify(c, accessToken) + if err != nil { + api.AddError(err) + api.Err(http.StatusUnauthorized) + return + } + user := &User{} + err = idToken.Claims(user) + if err != nil { + api.AddError(err) + api.Err(http.StatusUnauthorized) + return + } + //鉴权 + c.Set("user", user) + c.Next() + } +} + +// getTokenFromHeader 获取token +func getTokenFromHeader(c *gin.Context) string { + return strings.ReplaceAll(strings.ReplaceAll( + c.GetHeader("Authorization"), + "Bearer ", + ""), + "bearer", + "") +} + +// GetLoginUser 获取登录用户 +func GetLoginUser(c *gin.Context) *User { + user, ok := c.Get("user") + if !ok { + return nil + } + return user.(*User) +} diff --git a/pkg/response/actions/get_gorm.go b/pkg/response/actions/get_gorm.go index 4f26b89..6adbaa1 100644 --- a/pkg/response/actions/get_gorm.go +++ b/pkg/response/actions/get_gorm.go @@ -30,7 +30,7 @@ func NewGetGorm(m schema.Tabler, key string) *Get { func (e *Get) getGorm(c *gin.Context, key string) { api := response.Make(c) m := pkg.TablerDeepCopy(e.ModelGorm) - err := gormdb.DB.First(m, c.Param(key)).Error + err := gormdb.DB.First(m, "id = ?", c.Param(key)).Error if err != nil { api.Log.Error(err) api.AddError(err) diff --git a/pkg/response/actions/get_mgm.go b/pkg/response/actions/get_mgm.go index 4375874..21b2f3f 100644 --- a/pkg/response/actions/get_mgm.go +++ b/pkg/response/actions/get_mgm.go @@ -44,8 +44,8 @@ func (*Get) String() string { // Handler action handler func (e *Get) Handler() gin.HandlerFunc { return func(c *gin.Context) { - if e.ModelMgm != nil { - e.getMgm(c, e.Key) + if e.ModelGorm != nil { + e.getGorm(c, e.Key) return } if e.ModelMgm != nil { diff --git a/pkg/response/model.go b/pkg/response/model.go index fcaf01c..7cc1428 100644 --- a/pkg/response/model.go +++ b/pkg/response/model.go @@ -17,7 +17,7 @@ type Response struct { type response struct { Response - Data interface{} `json:"data"` + List interface{} `json:"list,omitempty"` } // Page page @@ -34,7 +34,7 @@ type page struct { // SetData set data func (e *response) SetData(data interface{}) { - e.Data = data + e.List = data } // Clone clone diff --git a/pkg/response/return.go b/pkg/response/return.go index 9d6d042..3585043 100644 --- a/pkg/response/return.go +++ b/pkg/response/return.go @@ -42,15 +42,15 @@ func OK(c *gin.Context, data interface{}, msg ...string) { switch c.Request.Method { case http.MethodDelete: res.SetCode(http.StatusNoContent) - c.AbortWithStatusJSON(http.StatusNoContent, res) + c.AbortWithStatusJSON(http.StatusNoContent, data) return case http.MethodPost: res.SetCode(http.StatusCreated) - c.AbortWithStatusJSON(http.StatusCreated, res) + c.AbortWithStatusJSON(http.StatusCreated, data) return default: res.SetCode(http.StatusOK) - c.AbortWithStatusJSON(http.StatusOK, res) + c.AbortWithStatusJSON(http.StatusOK, data) } } @@ -62,9 +62,9 @@ func PageOK(c *gin.Context, result interface{}, count int64, pageIndex int64, pa res.Current = pageIndex res.PageSize = pageSize res.response.SetData(result) - res.response.SetMsg(msg...) + //res.response.SetMsg(msg...) res.response.SetTraceID(pkg.GenerateMsgIDFromContext(c)) - res.response.SetCode(http.StatusOK) + //res.response.SetCode(http.StatusOK) c.Set("result", res) c.Set("status", http.StatusOK) c.AbortWithStatusJSON(http.StatusOK, res) diff --git a/pkg/security/security.go b/pkg/security/security.go index 732a0e9..9703128 100644 --- a/pkg/security/security.go +++ b/pkg/security/security.go @@ -14,5 +14,5 @@ type Verifier interface { GetRoleID() string GetEmail() string GetUsername() string - Verify(tenantID string, username string, password string) (bool, Verifier, error) + Verify() (bool, Verifier, error) } diff --git a/pkg/store/oauth2.go b/pkg/store/oauth2.go new file mode 100644 index 0000000..4568d80 --- /dev/null +++ b/pkg/store/oauth2.go @@ -0,0 +1,32 @@ +package store + +/* + * @Author: lwnmengjing + * @Date: 2022/4/21 17:20 + * @Last Modified by: lwnmengjing + * @Last Modified time: 2022/4/21 17:20 + */ + +import ( + "context" + + "golang.org/x/oauth2" +) + +// DefaultOAuth2Store default oauth2 store +var DefaultOAuth2Store OAuth2Store + +// OAuth2Store is the interface for OAuth2 configuration. +type OAuth2Store interface { + GetClientByDomain(c context.Context, domain string) (OAuth2Configure, error) +} + +// OAuth2Configure is the interface for OAuth2 configuration. +type OAuth2Configure interface { + GetOAuth2Config(c context.Context) (*oauth2.Config, error) + GetIssuer() string + GetClientID() string + GetClientSecret() string + GetRedirectURL() string + GetScopes() []string +} From 5bd9a8c7487fd56de34d7ad951406b4c4e85332b Mon Sep 17 00:00:00 2001 From: lwnmengjing Date: Mon, 14 Aug 2023 01:40:15 +0800 Subject: [PATCH 2/3] :sparkles: feat: change response --- pkg/response/actions/get_gorm.go | 6 ++++++ pkg/response/model.go | 14 +++++++------- pkg/response/return.go | 10 ++++------ pkg/response/type.go | 6 +++--- 4 files changed, 20 insertions(+), 16 deletions(-) diff --git a/pkg/response/actions/get_gorm.go b/pkg/response/actions/get_gorm.go index 6adbaa1..a230df2 100644 --- a/pkg/response/actions/get_gorm.go +++ b/pkg/response/actions/get_gorm.go @@ -8,6 +8,8 @@ package actions */ import ( + "errors" + "gorm.io/gorm" "net/http" "github.com/gin-gonic/gin" @@ -32,6 +34,10 @@ func (e *Get) getGorm(c *gin.Context, key string) { m := pkg.TablerDeepCopy(e.ModelGorm) err := gormdb.DB.First(m, "id = ?", c.Param(key)).Error if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + api.Err(http.StatusNotFound) + return + } api.Log.Error(err) api.AddError(err) api.Err(http.StatusInternalServerError) diff --git a/pkg/response/model.go b/pkg/response/model.go index 7cc1428..011e8e9 100644 --- a/pkg/response/model.go +++ b/pkg/response/model.go @@ -1,13 +1,14 @@ package response import ( - "strconv" "strings" ) // Response response type Response struct { Success bool `json:"success,omitempty"` + Status string `json:"status,omitempty"` + Code int `json:"code,omitempty"` ErrorCode string `json:"errorCode,omitempty"` ErrorMessage string `json:"errorMessage,omitempty"` ShowType uint8 `json:"showType,omitempty"` @@ -33,7 +34,7 @@ type page struct { } // SetData set data -func (e *response) SetData(data interface{}) { +func (e *response) SetList(data interface{}) { e.List = data } @@ -54,11 +55,10 @@ func (e *response) SetMsg(s ...string) { } // SetCode set code -func (e *response) SetCode(code int32) { - e.ErrorCode = strconv.Itoa(int(code)) +func (e *response) SetCode(code int) { + e.Code = code } -// SetSuccess set success -func (e *response) SetSuccess(success bool) { - e.Success = success +func (e *response) SetStatus(status string) { + e.Status = status } diff --git a/pkg/response/return.go b/pkg/response/return.go index 3585043..53fde53 100644 --- a/pkg/response/return.go +++ b/pkg/response/return.go @@ -24,8 +24,8 @@ func Error(c *gin.Context, code int, err error, msg ...string) { } res.SetMsg(msg...) res.SetTraceID(pkg.GenerateMsgIDFromContext(c)) - res.SetCode(int32(code)) - res.SetSuccess(false) + res.SetCode(code) + res.SetStatus("error") c.Set("result", res) c.Set("status", code) c.AbortWithStatusJSON(code, res) @@ -35,9 +35,7 @@ func Error(c *gin.Context, code int, err error, msg ...string) { func OK(c *gin.Context, data interface{}, msg ...string) { checkContext(c) res := Default.Clone() - res.SetData(data) - res.SetSuccess(true) - res.SetMsg(msg...) + res.SetList(data) res.SetTraceID(pkg.GenerateMsgIDFromContext(c)) switch c.Request.Method { case http.MethodDelete: @@ -61,7 +59,7 @@ func PageOK(c *gin.Context, result interface{}, count int64, pageIndex int64, pa res.Count = count res.Current = pageIndex res.PageSize = pageSize - res.response.SetData(result) + res.response.SetList(result) //res.response.SetMsg(msg...) res.response.SetTraceID(pkg.GenerateMsgIDFromContext(c)) //res.response.SetCode(http.StatusOK) diff --git a/pkg/response/type.go b/pkg/response/type.go index e06f258..bea67a5 100644 --- a/pkg/response/type.go +++ b/pkg/response/type.go @@ -9,10 +9,10 @@ package response // Responses responses type Responses interface { - SetCode(int32) + SetCode(int) SetTraceID(string) SetMsg(...string) - SetData(interface{}) - SetSuccess(bool) + SetList(interface{}) + SetStatus(string) Clone() Responses } From 346f3c6959a849e823000c19cbca6e923a83fa1a Mon Sep 17 00:00:00 2001 From: lwnmengjing Date: Tue, 29 Aug 2023 09:41:59 +0800 Subject: [PATCH 3/3] :sparkles: feat: support casbin --- pkg/config/gormdb/gorm.go | 8 ++++---- pkg/response/actions/type.go | 5 +++++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/pkg/config/gormdb/gorm.go b/pkg/config/gormdb/gorm.go index 991a15a..476be18 100644 --- a/pkg/config/gormdb/gorm.go +++ b/pkg/config/gormdb/gorm.go @@ -95,7 +95,7 @@ func (e *Database) Init() { if e.CasbinModel != "" { //set casbin adapter var a persist.Adapter - a, err = gormadapter.NewAdapterByDB(DB) + a, err = gormadapter.NewAdapterByDBUseTableName(DB, "mss_boot", "casbin_rule") if err != nil { log.Fatalf("gormadapter.NewAdapterByDB error : %s", err.Error()) } @@ -112,8 +112,8 @@ func (e *Database) Init() { if err != nil { log.Fatalf("Enforcer.LoadPolicy error : %s", err.Error()) } - //Enforcer.EnableAutoSave(true) - //Enforcer.EnableAutoBuildRoleLinks(true) - //Enforcer.EnableLog(true) + Enforcer.EnableAutoSave(true) + Enforcer.EnableAutoBuildRoleLinks(true) + Enforcer.EnableLog(true) } } diff --git a/pkg/response/actions/type.go b/pkg/response/actions/type.go index 91c8d7e..41bc990 100644 --- a/pkg/response/actions/type.go +++ b/pkg/response/actions/type.go @@ -46,6 +46,11 @@ type ModelGorm struct { DeletedAt gorm.DeletedAt `gorm:"index" bson:"-" json:"-"` } +func (e *ModelGorm) BeforeCreate(_ *gorm.DB) (err error) { + _, err = e.PrepareID(nil) + return err +} + // PrepareID prepare id func (e *ModelGorm) PrepareID(_ any) (any, error) { if e.ID == "" {