Просмотр исходного кода

获取登录用户信息,工具包完善

yxh 4 лет назад
Родитель
Сommit
318db649b2

+ 12 - 0
api/v1/system/user.go

@@ -12,6 +12,7 @@ type UserLoginReq struct {
 	VerifyCode string `p:"verifyCode" v:"required#验证码不能为空"`
 	VerifyKey  string `p:"verifyKey"`
 }
+
 type UserLoginRes struct {
 	g.Meta      `mime:"application/json" example:""`
 	UserInfo    *model.LoginUserRes `json:"userInfo"`
@@ -19,3 +20,14 @@ type UserLoginRes struct {
 	MenuList    []*model.UserMenus  `json:"menuList"`
 	Permissions []string            `json:"permissions"`
 }
+
+type UserMenusReq struct {
+	g.Meta        `path:"/getUserMenus" tags:"getUserMenus" method:"get" summary:"获取用户菜单"`
+	Authorization string `p:"Authorization" in:"header" dc:"Bearer {{token}}"`
+}
+
+type UserMenusRes struct {
+	g.Meta      `mime:"application/json" example:""`
+	MenuList    []*model.UserMenus `json:"menuList"`
+	Permissions []string           `json:"permissions"`
+}

+ 2 - 4
go.mod

@@ -4,11 +4,9 @@ go 1.15
 
 require (
 	github.com/casbin/casbin/v2 v2.42.0
-	github.com/gogf/gf/v2 v2.0.3
+	github.com/gogf/gf/v2 v2.0.4
 	github.com/mojocn/base64Captcha v1.3.5
 	github.com/mssola/user_agent v0.5.3
 	github.com/tiger1103/gfast-cache v0.0.6
-	github.com/tiger1103/gfast-token v0.0.4
+	github.com/tiger1103/gfast-token v0.0.7
 )
-
-replace github.com/gogf/gf/v2 v2.0.3 => github.com/tiger1103/gf/v2 v2.0.4

+ 4 - 4
go.sum

@@ -26,6 +26,8 @@ github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LB
 github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
 github.com/gogf/gf/v2 v2.0.0-rc.0.20220117131058-9345eb5e946f/go.mod h1:apktt6TleWtCIwpz63vBqUnw8MX8gWKoZyxgDpXFtgM=
 github.com/gogf/gf/v2 v2.0.0-rc3/go.mod h1:apktt6TleWtCIwpz63vBqUnw8MX8gWKoZyxgDpXFtgM=
+github.com/gogf/gf/v2 v2.0.4 h1:m5F/f2lX+etXhI6rAQCR6szQoHE1ulAYlIvH0UXa2wM=
+github.com/gogf/gf/v2 v2.0.4/go.mod h1:apktt6TleWtCIwpz63vBqUnw8MX8gWKoZyxgDpXFtgM=
 github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
 github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
 github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
@@ -83,12 +85,10 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
 github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
 github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/tiger1103/gf/v2 v2.0.4 h1:RQmqPGIhRi6Y3ZLvMz/0OVZHiKXFuf+Ly3d1bEPDaTg=
-github.com/tiger1103/gf/v2 v2.0.4/go.mod h1:apktt6TleWtCIwpz63vBqUnw8MX8gWKoZyxgDpXFtgM=
 github.com/tiger1103/gfast-cache v0.0.6 h1:H40Txv6co5bjaRlFQ/ixcMfpgsihhPpNftvCHm7hLpI=
 github.com/tiger1103/gfast-cache v0.0.6/go.mod h1:s6cRWyr87wz6IJNGKRV6Ahq9hcuVz8h2PAtGrO66JO8=
-github.com/tiger1103/gfast-token v0.0.4 h1:h59pbFd/VyORNunsDdzIdqJkZIHYHsYCzdmPbcqGkZs=
-github.com/tiger1103/gfast-token v0.0.4/go.mod h1:RnVRqmWD3h4qfTU1vJNXNhQjh2L5ht1vxLnjwShwGuY=
+github.com/tiger1103/gfast-token v0.0.7 h1:LJxa4wvtorig/aiXcrqbO9BpJwuGVNAgp1fQVcR0Des=
+github.com/tiger1103/gfast-token v0.0.7/go.mod h1:RnVRqmWD3h4qfTU1vJNXNhQjh2L5ht1vxLnjwShwGuY=
 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
 go.opentelemetry.io/otel v1.0.0 h1:qTTn6x71GVBvoafHK/yaRUmFzI4LcONZD0/kXxl5PHI=

+ 1 - 0
internal/app/common/service/token.go

@@ -19,6 +19,7 @@ type IGfToken interface {
 	GenerateToken(ctx context.Context, key string, data interface{}) (keys string, err error)
 	Middleware(group *ghttp.RouterGroup) error
 	ParseToken(r *ghttp.Request) (*gftoken.CustomClaims, error)
+	IsLogin(r *ghttp.Request) (b bool, failed *gftoken.AuthFailed)
 }
 
 type gfTokenImpl struct {

+ 13 - 0
internal/app/system/consts/context.go

@@ -0,0 +1,13 @@
+/*
+* @desc:context 相关常量
+* @company:云南奇讯科技有限公司
+* @Author: yixiaohu<yxh669@qq.com>
+* @Date:   2022/3/16 14:52
+ */
+
+package consts
+
+const (
+	// CtxKey 上下文变量存储键名,前后端系统共享
+	CtxKey = "GFastContext"
+)

+ 22 - 2
internal/app/system/controller/base.go

@@ -8,9 +8,13 @@
 package controller
 
 import (
-	"github.com/gogf/gf/v2/frame/g"
 	"github.com/gogf/gf/v2/net/ghttp"
+	"github.com/gogf/gf/v2/util/gconv"
+	"github.com/tiger1103/gfast-token/gftoken"
 	commonController "github.com/tiger1103/gfast/v3/internal/app/common/controller"
+	"github.com/tiger1103/gfast/v3/internal/app/system/model"
+	"github.com/tiger1103/gfast/v3/internal/app/system/service"
+	"github.com/tiger1103/gfast/v3/library/libResponse"
 )
 
 type baseController struct {
@@ -20,5 +24,21 @@ type baseController struct {
 // Init 自动执行的初始化方法
 func (c *baseController) Init(r *ghttp.Request) {
 	c.BaseController.Init(r)
-	g.Log().Debug(r.GetCtx(), "system.baseController.Init")
+	ctx := r.GetCtx()
+	// 验证用户是否登录
+	b, failed := service.GfToken(ctx).IsLogin(r)
+	if !b {
+		libResponse.JsonExit(r, failed.Code, failed.Message)
+	}
+	// 初始化登录用户信息
+	data, err := service.GfToken(ctx).ParseToken(r)
+	if err != nil {
+		libResponse.JsonExit(r, gftoken.FailedAuthCode, err.Error())
+	}
+	context := new(model.Context)
+	err = gconv.Struct(data.Data, &context.User)
+	if err != nil {
+		libResponse.JsonExit(r, gftoken.FailedAuthCode, err.Error())
+	}
+	service.Context().Init(r, context)
 }

+ 8 - 0
internal/app/system/controller/menu.go

@@ -0,0 +1,8 @@
+/*
+* @desc:菜单
+* @company:云南奇讯科技有限公司
+* @Author: yixiaohu
+* @Date:   2022/3/16 10:36
+ */
+
+package controller

+ 16 - 0
internal/app/system/controller/user.go

@@ -69,6 +69,7 @@ func (c *UserController) Login(ctx context.Context, req *system.UserLoginReq) (r
 	if g.Cfg().MustGet(ctx, "gfToken.multiLogin").Bool() {
 		key = gconv.String(user.Id) + "-" + gmd5.MustEncryptString(user.UserName) + gmd5.MustEncryptString(user.UserPassword+ip+userAgent)
 	}
+	user.UserPassword = ""
 	token, err = service.GfToken(ctx).GenerateToken(ctx, key, user)
 	if err != nil {
 		return
@@ -86,3 +87,18 @@ func (c *UserController) Login(ctx context.Context, req *system.UserLoginReq) (r
 	}
 	return
 }
+
+// GetUserMenus 获取用户菜单及按钮权限
+func (c *UserController) GetUserMenus(ctx context.Context, req *system.UserMenusReq) (res *system.UserMenusRes, err error) {
+	var (
+		permissions []string
+		menuList    []*model.UserMenus
+	)
+	userId := service.Context().GetUserId(ctx)
+	menuList, permissions, err = service.User().GetAdminRules(ctx, userId)
+	res = &system.UserMenusRes{
+		MenuList:    menuList,
+		Permissions: permissions,
+	}
+	return
+}

+ 16 - 0
internal/app/system/model/context.go

@@ -0,0 +1,16 @@
+/*
+* @desc:context-model
+* @company:云南奇讯科技有限公司
+* @Author: yixiaohu
+* @Date:   2022/3/16 14:45
+ */
+
+package model
+
+type Context struct {
+	User *ContextUser // User in context.
+}
+
+type ContextUser struct {
+	*LoginUserRes
+}

+ 11 - 9
internal/app/system/model/sys_auth_rule.go

@@ -35,18 +35,20 @@ type UserMenu struct {
 	MenuName  string `json:"menuName"`
 	Component string `json:"component"`
 	Path      string `json:"path"`
-	Meta      struct {
-		Icon        string `json:"icon"`
-		Title       string `json:"title"`
-		IsLink      string `json:"isLink"`
-		IsHide      bool   `json:"isHide"`
-		IsKeepAlive bool   `json:"isKeepAlive"`
-		IsAffix     bool   `json:"isAffix"`
-		IsIframe    bool   `json:"isIframe"`
-	} `json:"meta"`
+	*MenuMeta `json:"meta"`
 }
 
 type UserMenus struct {
 	*UserMenu `json:""`
 	Children  []*UserMenus `json:"children"`
 }
+
+type MenuMeta struct {
+	Icon        string `json:"icon"`
+	Title       string `json:"title"`
+	IsLink      string `json:"isLink"`
+	IsHide      bool   `json:"isHide"`
+	IsKeepAlive bool   `json:"isKeepAlive"`
+	IsAffix     bool   `json:"isAffix"`
+	IsIframe    bool   `json:"isIframe"`
+}

+ 68 - 0
internal/app/system/service/context.go

@@ -0,0 +1,68 @@
+/*
+* @desc:context-service
+* @company:云南奇讯科技有限公司
+* @Author: yixiaohu
+* @Date:   2022/3/16 14:46
+ */
+
+package service
+
+import (
+	"context"
+	"github.com/gogf/gf/v2/net/ghttp"
+	"github.com/tiger1103/gfast/v3/internal/app/system/consts"
+	"github.com/tiger1103/gfast/v3/internal/app/system/model"
+)
+
+type IContext interface {
+	Init(r *ghttp.Request, customCtx *model.Context)
+	Get(ctx context.Context) *model.Context
+	SetUser(ctx context.Context, ctxUser *model.ContextUser)
+	GetLoginUser(ctx context.Context) *model.ContextUser
+	GetUserId(ctx context.Context) uint64
+}
+
+// Context 上下文管理服务
+var contextImpl = contextServiceImpl{}
+
+type contextServiceImpl struct{}
+
+func Context() IContext {
+	return IContext(&contextImpl)
+}
+
+// Init 初始化上下文对象指针到上下文对象中,以便后续的请求流程中可以修改。
+func (s *contextServiceImpl) Init(r *ghttp.Request, customCtx *model.Context) {
+	r.SetCtxVar(consts.CtxKey, customCtx)
+}
+
+// Get 获得上下文变量,如果没有设置,那么返回nil
+func (s *contextServiceImpl) Get(ctx context.Context) *model.Context {
+	value := ctx.Value(consts.CtxKey)
+	if value == nil {
+		return nil
+	}
+	if localCtx, ok := value.(*model.Context); ok {
+		return localCtx
+	}
+	return nil
+}
+
+// SetUser 将上下文信息设置到上下文请求中,注意是完整覆盖
+func (s *contextServiceImpl) SetUser(ctx context.Context, ctxUser *model.ContextUser) {
+	s.Get(ctx).User = ctxUser
+}
+
+// GetLoginUser 获取当前登陆用户信息
+func (s *contextServiceImpl) GetLoginUser(ctx context.Context) *model.ContextUser {
+	context := s.Get(ctx)
+	if context == nil {
+		return nil
+	}
+	return context.User
+}
+
+// GetUserId 获取当前登录用户id
+func (s *contextServiceImpl) GetUserId(ctx context.Context) uint64 {
+	return s.GetLoginUser(ctx).Id
+}

+ 6 - 22
internal/app/system/service/sys_user.go

@@ -258,23 +258,7 @@ func (s *userImpl) setMenuData(menu *model.UserMenu, entity *model.SysAuthRuleIn
 		Index:    entity.Name,
 		Name:     gstr.UcFirst(entity.Path),
 		MenuName: entity.Title,
-		Meta: struct {
-			Icon        string `json:"icon"`
-			Title       string `json:"title"`
-			IsLink      string `json:"isLink"`
-			IsHide      bool   `json:"isHide"`
-			IsKeepAlive bool   `json:"isKeepAlive"`
-			IsAffix     bool   `json:"isAffix"`
-			IsIframe    bool   `json:"isIframe"`
-		}(struct {
-			Icon        string
-			Title       string
-			IsLink      string
-			IsHide      bool
-			IsKeepAlive bool
-			IsAffix     bool
-			IsIframe    bool
-		}{
+		MenuMeta: &model.MenuMeta{
 			Icon:        entity.Icon,
 			Title:       entity.Title,
 			IsLink:      "",
@@ -282,7 +266,7 @@ func (s *userImpl) setMenuData(menu *model.UserMenu, entity *model.SysAuthRuleIn
 			IsKeepAlive: false,
 			IsAffix:     false,
 			IsIframe:    false,
-		}),
+		},
 	}
 	if entity.MenuType != 0 {
 		menu.Component = entity.Component
@@ -292,14 +276,14 @@ func (s *userImpl) setMenuData(menu *model.UserMenu, entity *model.SysAuthRuleIn
 		menu.Path = "/" + entity.Path
 	}
 	if entity.AlwaysShow == 1 {
-		menu.Meta.IsHide = false
+		menu.MenuMeta.IsHide = false
 	} else {
-		menu.Meta.IsHide = true
+		menu.MenuMeta.IsHide = true
 	}
 	if entity.AlwaysShow == 1 && entity.MenuType == 0 {
-		menu.Meta.IsHide = true
+		menu.MenuMeta.IsHide = true
 	} else {
-		menu.Meta.IsHide = false
+		menu.MenuMeta.IsHide = false
 	}
 	return menu
 }

+ 0 - 1
internal/cmd/cmd.go

@@ -18,7 +18,6 @@ var (
 		Func: func(ctx context.Context, parser *gcmd.Parser) (err error) {
 			s := g.Server()
 			s.Group("/", func(group *ghttp.RouterGroup) {
-				group.Middleware(ghttp.MiddlewareHandlerResponse)
 				router.BindController(group)
 			})
 			enhanceOpenAPIDoc(s)

+ 10 - 6
internal/router/router.go

@@ -15,10 +15,14 @@ import (
 )
 
 func BindController(group *ghttp.RouterGroup) {
-	// 绑定后台路由
-	systemRouter.BindController(group)
-	// 绑定测试路由
-	demoRouter.BindController(group)
-	// 绑定公共路由
-	commonRouter.BindController(group)
+	group.Group("/api/v1", func(group *ghttp.RouterGroup) {
+		group.Middleware(ghttp.MiddlewareHandlerResponse)
+		// 绑定后台路由
+		systemRouter.BindController(group)
+		// 绑定测试路由
+		demoRouter.BindController(group)
+		// 绑定公共路由
+		commonRouter.BindController(group)
+	})
+
 }

+ 107 - 0
library/libResponse/response.go

@@ -0,0 +1,107 @@
+package libResponse
+
+import (
+	"github.com/gogf/gf/v2/frame/g"
+	"github.com/gogf/gf/v2/net/ghttp"
+	"github.com/gogf/gf/v2/os/gview"
+	"github.com/gogf/gf/v2/text/gstr"
+	"github.com/gogf/gf/v2/util/gconv"
+)
+
+const (
+	SuccessCode int = 0
+	ErrorCode   int = -1
+)
+
+type Response struct {
+	// 代码
+	Code int `json:"code" example:"200"`
+	// 数据集
+	Data interface{} `json:"data"`
+	// 消息
+	Msg string `json:"msg"`
+}
+
+var response = new(Response)
+
+func JsonExit(r *ghttp.Request, code int, msg string, data ...interface{}) {
+	response.JsonExit(r, code, msg, data...)
+}
+
+func RJson(r *ghttp.Request, code int, msg string, data ...interface{}) {
+	response.RJson(r, code, msg, data...)
+}
+
+func SusJson(isExit bool, r *ghttp.Request, msg string, data ...interface{}) {
+	response.SusJson(isExit, r, msg, data...)
+}
+
+func FailJson(isExit bool, r *ghttp.Request, msg string, data ...interface{}) {
+	response.FailJson(isExit, r, msg, data...)
+}
+
+func WriteTpl(r *ghttp.Request, tpl string, view *gview.View, params ...gview.Params) error {
+	return response.WriteTpl(r, tpl, view, params...)
+}
+
+// 返回JSON数据并退出当前HTTP执行函数。
+func (res *Response) JsonExit(r *ghttp.Request, code int, msg string, data ...interface{}) {
+	res.RJson(r, code, msg, data...)
+	r.Exit()
+}
+
+// 标准返回结果数据结构封装。
+// 返回固定数据结构的JSON:
+// code:  状态码(200:成功,302跳转,和http请求状态码一至);
+// msg:  请求结果信息;
+// data: 请求结果,根据不同接口返回结果的数据结构不同;
+func (res *Response) RJson(r *ghttp.Request, code int, msg string, data ...interface{}) {
+	responseData := interface{}(nil)
+	if len(data) > 0 {
+		responseData = data[0]
+	}
+	response = &Response{
+		Code: code,
+		Msg:  msg,
+		Data: responseData,
+	}
+	r.Response.WriteJson(response)
+}
+
+//成功返回JSON
+func (res *Response) SusJson(isExit bool, r *ghttp.Request, msg string, data ...interface{}) {
+	if isExit {
+		res.JsonExit(r, SuccessCode, msg, data...)
+	}
+	res.RJson(r, SuccessCode, msg, data...)
+}
+
+//失败返回JSON
+func (res *Response) FailJson(isExit bool, r *ghttp.Request, msg string, data ...interface{}) {
+	if isExit {
+		res.JsonExit(r, ErrorCode, msg, data...)
+	}
+	res.RJson(r, ErrorCode, msg, data...)
+}
+
+// WriteTpl 模板输出
+func (res *Response) WriteTpl(r *ghttp.Request, tpl string, view *gview.View, params ...gview.Params) error {
+	//绑定模板中需要用到的方法
+	view.BindFuncMap(g.Map{
+		// 根据长度i来切割字符串
+		"subStr": func(str interface{}, i int) (s string) {
+			s1 := gconv.String(str)
+			if gstr.LenRune(s1) > i {
+				s = gstr.SubStrRune(s1, 0, i) + "..."
+				return s
+			}
+			return s1
+		},
+	})
+	r.Response.Write(view.Parse(r.GetCtx(), tpl, params...))
+	return nil
+}
+
+func (res *Response) Redirect(r *ghttp.Request, location string, code ...int) {
+	r.Response.RedirectTo(location, code...)
+}

+ 2 - 0
manifest/config/config.yaml

@@ -46,6 +46,8 @@ gfToken:
   multiLogin: true
   encryptKey: "49c54195e750b04e74a8429b17896586"
   cacheModel: "redis"
+  excludePaths:
+    - "/api/v1/system/login"
 
 # Redis 配置示例
 redis: