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

+ 2 - 1
apiv1/system/user.go

@@ -14,5 +14,6 @@ type UserLoginReq struct {
 }
 type UserLoginRes struct {
 	g.Meta   `mime:"text/html" example:""`
-	UserInfo *model.LoginUserRes
+	UserInfo *model.LoginUserRes `json:"userInfo"`
+	Token    string              `json:"token"`
 }

+ 3 - 1
go.mod

@@ -4,5 +4,7 @@ go 1.15
 
 require (
 	github.com/gogf/gf/v2 v2.0.0-rc3
-	github.com/mojocn/base64Captcha v1.3.5 // indirect
+	github.com/mojocn/base64Captcha v1.3.5
+	github.com/mssola/user_agent v0.5.3
+	github.com/tiger1103/gfast-token v0.0.3
 )

+ 8 - 0
go.sum

@@ -22,6 +22,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-rc3 h1:FkmLFhgOCZnyr24H/Yj9V1psS7fJ79DtPuSz+l/kwsc=
 github.com/gogf/gf/v2 v2.0.0-rc3/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=
 github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
 github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -54,6 +56,8 @@ github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/Qd
 github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
 github.com/mojocn/base64Captcha v1.3.5 h1:Qeilr7Ta6eDtG4S+tQuZ5+hO+QHbiGAJdi4PfoagaA0=
 github.com/mojocn/base64Captcha v1.3.5/go.mod h1:/tTTXn4WTpX9CfrmipqRytCpJ27Uw3G6I7NcP2WwcmY=
+github.com/mssola/user_agent v0.5.3 h1:lBRPML9mdFuIZgI2cmlQ+atbpJdLdeVl2IDodjBR578=
+github.com/mssola/user_agent v0.5.3/go.mod h1:TTPno8LPY3wAIEKRpAtkdMT0f8SE24pLRGPahjCH4uw=
 github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
 github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
 github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
@@ -73,6 +77,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/gfast-token v0.0.2 h1:z2WfVAQV4ocKJeAL90pMVW2ryHVv2pijFG1GZffQ/EI=
+github.com/tiger1103/gfast-token v0.0.2/go.mod h1:RnVRqmWD3h4qfTU1vJNXNhQjh2L5ht1vxLnjwShwGuY=
+github.com/tiger1103/gfast-token v0.0.3 h1:uXZMeNXSb5j/GNLHAgkhwzhG/1M3eL3Ho0RNfl5LUpw=
+github.com/tiger1103/gfast-token v0.0.3/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=

+ 37 - 0
internal/app/common/model/token.go

@@ -0,0 +1,37 @@
+/*
+* @desc:token options
+* @company:云南奇讯科技有限公司
+* @Author: yixiaohu
+* @Date:   2022/3/8 16:02
+ */
+
+package model
+
+import (
+	"github.com/gogf/gf/v2/frame/g"
+)
+
+const (
+	CacheModelMem   = "memory"
+	CacheModelRedis = "redis"
+)
+
+type TokenOptions struct {
+	//  server name
+	ServerName string `json:"serverName"`
+	// 缓存key (每创建一个实例CacheKey必须不相同)
+	CacheKey string `json:"cacheKey"`
+	// 超时时间 默认10天(秒)
+	Timeout int64 `json:"timeout"`
+	// 缓存刷新时间 默认5天(秒)
+	// 处理携带token的请求时当前时间大于超时时间并小于缓存刷新时间时token将自动刷新即重置token存活时间
+	// MaxRefresh值为0时,token将不会自动刷新
+	MaxRefresh int64 `json:"maxRefresh"`
+	// 是否允许多点登录
+	MultiLogin bool `json:"multiLogin"`
+	// Token加密key 32位
+	EncryptKey []byte `json:"encryptKey"`
+	// 拦截排除地址
+	ExcludePaths g.SliceStr `json:"excludePaths"`
+	CacheModel   string     `json:"cacheModel"`
+}

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

@@ -0,0 +1,51 @@
+/*
+* @desc:token功能
+* @company:云南奇讯科技有限公司
+* @Author: yixiaohu
+* @Date:   2022/3/8 15:54
+ */
+
+package service
+
+import (
+	"context"
+	"github.com/gogf/gf/v2/net/ghttp"
+	"github.com/tiger1103/gfast-token/gftoken"
+	"github.com/tiger1103/gfast/v3/internal/app/common/model"
+)
+
+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)
+}
+
+type gfTokenImpl struct {
+	*gftoken.GfToken
+}
+
+var gT = gfTokenImpl{
+	GfToken: gftoken.NewGfToken(),
+}
+
+func GfToken(options *model.TokenOptions) IGfToken {
+	if options.CacheModel == model.CacheModelRedis {
+		gT.GfToken = gftoken.NewGfToken(
+			gftoken.WithCacheKey(options.CacheKey),
+			gftoken.WithTimeout(options.Timeout),
+			gftoken.WithMaxRefresh(options.MaxRefresh),
+			gftoken.WithMultiLogin(options.MultiLogin),
+			gftoken.WithExcludePaths(options.ExcludePaths),
+			gftoken.WithGRedis(),
+		)
+	} else {
+		gT.GfToken = gftoken.NewGfToken(
+			gftoken.WithCacheKey(options.CacheKey),
+			gftoken.WithTimeout(options.Timeout),
+			gftoken.WithMaxRefresh(options.MaxRefresh),
+			gftoken.WithMultiLogin(options.MultiLogin),
+			gftoken.WithExcludePaths(options.ExcludePaths),
+		)
+	}
+	return IGfToken(&gT)
+}

+ 40 - 3
internal/app/system/controller/user.go

@@ -2,12 +2,15 @@ package controller
 
 import (
 	"context"
+	"github.com/gogf/gf/v2/crypto/gmd5"
 	"github.com/gogf/gf/v2/errors/gerror"
 	"github.com/gogf/gf/v2/os/genv"
+	"github.com/gogf/gf/v2/util/gconv"
 	"github.com/tiger1103/gfast/v3/apiv1/system"
 	commonService "github.com/tiger1103/gfast/v3/internal/app/common/service"
 	"github.com/tiger1103/gfast/v3/internal/app/system/model"
 	"github.com/tiger1103/gfast/v3/internal/app/system/service"
+	"github.com/tiger1103/gfast/v3/library/libUtils"
 )
 
 var (
@@ -19,7 +22,10 @@ type UserController struct {
 }
 
 func (c *UserController) Login(ctx context.Context, req *system.UserLoginReq) (res *system.UserLoginRes, err error) {
-	var user *model.LoginUserRes
+	var (
+		user  *model.LoginUserRes
+		token string
+	)
 	//判断验证码是否正确
 	debug := genv.GetWithCmd("gf.debug")
 	if debug.Int() != 1 {
@@ -28,14 +34,45 @@ func (c *UserController) Login(ctx context.Context, req *system.UserLoginReq) (r
 			return
 		}
 	}
-	//ip := libUtils.GetClientIp(ctx)
-	//userAgent := libUtils.GetUserAgent(ctx)
+	ip := libUtils.GetClientIp(ctx)
+	userAgent := libUtils.GetUserAgent(ctx)
 	user, err = service.User().GetAdminUserByUsernamePassword(ctx, req)
+	if err != nil {
+		// 保存登录失败的日志信息
+		service.SysLoginLog().Invoke(ctx, &model.LoginLogParams{
+			Status:    0,
+			Username:  req.Username,
+			Ip:        ip,
+			UserAgent: userAgent,
+			Msg:       err.Error(),
+			Module:    "系统后台",
+		})
+		return
+	}
+	err = service.User().UpdateLoginInfo(ctx, user.Id, ip)
+	if err != nil {
+		return
+	}
+	// 报存登录成功的日志信息
+	service.SysLoginLog().Invoke(ctx, &model.LoginLogParams{
+		Status:    1,
+		Username:  req.Username,
+		Ip:        ip,
+		UserAgent: userAgent,
+		Msg:       "登录成功",
+		Module:    "系统后台",
+	})
+	token, err = service.GfToken(ctx).GenerateToken(
+		ctx,
+		gconv.String(user.Id)+"-"+gmd5.MustEncryptString(user.UserName)+gmd5.MustEncryptString(user.UserPassword),
+		user,
+	)
 	if err != nil {
 		return
 	}
 	res = &system.UserLoginRes{
 		UserInfo: user,
+		Token:    token,
 	}
 	return
 }

+ 23 - 0
internal/app/system/model/entity/sys_login_log.go

@@ -0,0 +1,23 @@
+// =================================================================================
+// Code generated by GoFrame CLI tool. DO NOT EDIT. Created at 2022-03-08 11:31:48
+// =================================================================================
+
+package entity
+
+import (
+	"github.com/gogf/gf/v2/os/gtime"
+)
+
+// SysLoginLog is the golang structure for table sys_login_log.
+type SysLoginLog struct {
+	InfoId        int64       `json:"infoId"        description:"访问ID"`
+	LoginName     string      `json:"loginName"     description:"登录账号"`
+	Ipaddr        string      `json:"ipaddr"        description:"登录IP地址"`
+	LoginLocation string      `json:"loginLocation" description:"登录地点"`
+	Browser       string      `json:"browser"       description:"浏览器类型"`
+	Os            string      `json:"os"            description:"操作系统"`
+	Status        int         `json:"status"        description:"登录状态(0成功 1失败)"`
+	Msg           string      `json:"msg"           description:"提示消息"`
+	LoginTime     *gtime.Time `json:"loginTime"     description:"登录时间"`
+	Module        string      `json:"module"        description:"登录模块"`
+}

+ 18 - 0
internal/app/system/model/sys_user_login.go

@@ -0,0 +1,18 @@
+/*
+* @desc:登录日志
+* @company:云南奇讯科技有限公司
+* @Author: yixiaohu
+* @Date:   2022/3/8 11:43
+ */
+
+package model
+
+// LoginLogParams 登录日志写入参数
+type LoginLogParams struct {
+	Status    int
+	Username  string
+	Ip        string
+	UserAgent string
+	Msg       string
+	Module    string
+}

+ 90 - 0
internal/app/system/service/internal/dao/internal/sys_login_log.go

@@ -0,0 +1,90 @@
+// ==========================================================================
+// Code generated by GoFrame CLI tool. DO NOT EDIT. Created at 2022-03-08 11:31:48
+// ==========================================================================
+
+package internal
+
+import (
+	"context"
+	"github.com/gogf/gf/v2/database/gdb"
+	"github.com/gogf/gf/v2/frame/g"
+)
+
+// SysLoginLogDao is the data access object for table sys_login_log.
+type SysLoginLogDao struct {
+	table   string             // table is the underlying table name of the DAO.
+	group   string             // group is the database configuration group name of current DAO.
+	columns SysLoginLogColumns // columns contains all the column names of Table for convenient usage.
+}
+
+// SysLoginLogColumns defines and stores column names for table sys_login_log.
+type SysLoginLogColumns struct {
+	InfoId        string // 访问ID
+	LoginName     string // 登录账号
+	Ipaddr        string // 登录IP地址
+	LoginLocation string // 登录地点
+	Browser       string // 浏览器类型
+	Os            string // 操作系统
+	Status        string // 登录状态(0成功 1失败)
+	Msg           string // 提示消息
+	LoginTime     string // 登录时间
+	Module        string // 登录模块
+}
+
+//  sysLoginLogColumns holds the columns for table sys_login_log.
+var sysLoginLogColumns = SysLoginLogColumns{
+	InfoId:        "info_id",
+	LoginName:     "login_name",
+	Ipaddr:        "ipaddr",
+	LoginLocation: "login_location",
+	Browser:       "browser",
+	Os:            "os",
+	Status:        "status",
+	Msg:           "msg",
+	LoginTime:     "login_time",
+	Module:        "module",
+}
+
+// NewSysLoginLogDao creates and returns a new DAO object for table data access.
+func NewSysLoginLogDao() *SysLoginLogDao {
+	return &SysLoginLogDao{
+		group:   "default",
+		table:   "sys_login_log",
+		columns: sysLoginLogColumns,
+	}
+}
+
+// DB retrieves and returns the underlying raw database management object of current DAO.
+func (dao *SysLoginLogDao) DB() gdb.DB {
+	return g.DB(dao.group)
+}
+
+// Table returns the table name of current dao.
+func (dao *SysLoginLogDao) Table() string {
+	return dao.table
+}
+
+// Columns returns all column names of current dao.
+func (dao *SysLoginLogDao) Columns() SysLoginLogColumns {
+	return dao.columns
+}
+
+// Group returns the configuration group name of database of current dao.
+func (dao *SysLoginLogDao) Group() string {
+	return dao.group
+}
+
+// Ctx creates and returns the Model for current DAO, It automatically sets the context for current operation.
+func (dao *SysLoginLogDao) Ctx(ctx context.Context) *gdb.Model {
+	return dao.DB().Model(dao.table).Safe().Ctx(ctx)
+}
+
+// Transaction wraps the transaction logic using function f.
+// It rollbacks the transaction and returns the error from function f if it returns non-nil error.
+// It commits the transaction and returns nil if function f returns nil.
+//
+// Note that, you should not Commit or Rollback the transaction in function f
+// as it is automatically handled by this function.
+func (dao *SysLoginLogDao) Transaction(ctx context.Context, f func(ctx context.Context, tx *gdb.TX) error) (err error) {
+	return dao.Ctx(ctx).Transaction(ctx, f)
+}

+ 24 - 0
internal/app/system/service/internal/dao/sys_login_log.go

@@ -0,0 +1,24 @@
+// =================================================================================
+// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish.
+// =================================================================================
+
+package dao
+
+import (
+	"github.com/tiger1103/gfast/v3/internal/app/system/service/internal/dao/internal"
+)
+
+// sysLoginLogDao is the data access object for table sys_login_log.
+// You can define custom methods on it to extend its functionality as you wish.
+type sysLoginLogDao struct {
+	*internal.SysLoginLogDao
+}
+
+var (
+	// SysLoginLog is globally public accessible object for table sys_login_log operations.
+	SysLoginLog = sysLoginLogDao{
+		internal.NewSysLoginLogDao(),
+	}
+)
+
+// Fill with you ideas below.

+ 25 - 0
internal/app/system/service/internal/do/sys_login_log.go

@@ -0,0 +1,25 @@
+// =================================================================================
+// Code generated by GoFrame CLI tool. DO NOT EDIT. Created at 2022-03-08 11:31:48
+// =================================================================================
+
+package do
+
+import (
+	"github.com/gogf/gf/v2/frame/g"
+	"github.com/gogf/gf/v2/os/gtime"
+)
+
+// SysLoginLog is the golang structure of table sys_login_log for DAO operations like Where/Data.
+type SysLoginLog struct {
+	g.Meta        `orm:"table:sys_login_log, do:true"`
+	InfoId        interface{} // 访问ID
+	LoginName     interface{} // 登录账号
+	Ipaddr        interface{} // 登录IP地址
+	LoginLocation interface{} // 登录地点
+	Browser       interface{} // 浏览器类型
+	Os            interface{} // 操作系统
+	Status        interface{} // 登录状态(0成功 1失败)
+	Msg           interface{} // 提示消息
+	LoginTime     *gtime.Time // 登录时间
+	Module        interface{} // 登录模块
+}

+ 42 - 0
internal/app/system/service/sys_login_log.go

@@ -0,0 +1,42 @@
+/*
+* @desc:登录日志处理
+* @company:云南奇讯科技有限公司
+* @Author: yixiaohu
+* @Date:   2022/3/8 11:42
+ */
+
+package service
+
+import (
+	"context"
+	"github.com/gogf/gf/v2/os/grpool"
+	"github.com/tiger1103/gfast/v3/internal/app/system/model"
+)
+
+type ISysLoginLog interface {
+	Invoke(ctx context.Context, data *model.LoginLogParams)
+}
+
+type sysLoginLogImpl struct {
+	Pool *grpool.Pool
+}
+
+var (
+	sysLoginLog = sysLoginLogImpl{
+		Pool: grpool.New(100),
+	}
+)
+
+func SysLoginLog() ISysLoginLog {
+	return ISysLoginLog(&sysLoginLog)
+}
+
+func (s *sysLoginLogImpl) Invoke(ctx context.Context, data *model.LoginLogParams) {
+	s.Pool.Add(
+		ctx,
+		func(ctx context.Context) {
+			//写入日志数据
+			User().LoginLog(ctx, data)
+		},
+	)
+}

+ 39 - 2
internal/app/system/service/sys_user.go

@@ -11,9 +11,12 @@ import (
 	"context"
 	"github.com/gogf/gf/v2/errors/gerror"
 	"github.com/gogf/gf/v2/frame/g"
+	"github.com/gogf/gf/v2/os/gtime"
+	"github.com/mssola/user_agent"
 	"github.com/tiger1103/gfast/v3/apiv1/system"
 	"github.com/tiger1103/gfast/v3/internal/app/system/model"
 	"github.com/tiger1103/gfast/v3/internal/app/system/service/internal/dao"
+	"github.com/tiger1103/gfast/v3/internal/app/system/service/internal/do"
 	"github.com/tiger1103/gfast/v3/library/libUtils"
 	"github.com/tiger1103/gfast/v3/library/liberr"
 )
@@ -28,6 +31,8 @@ func User() IUser {
 
 type IUser interface {
 	GetAdminUserByUsernamePassword(ctx context.Context, req *system.UserLoginReq) (user *model.LoginUserRes, err error)
+	LoginLog(ctx context.Context, params *model.LoginLogParams)
+	UpdateLoginInfo(ctx context.Context, id uint64, ip string) (err error)
 }
 
 type userImpl struct {
@@ -54,8 +59,40 @@ func (s *userImpl) GetAdminUserByUsernamePassword(ctx context.Context, req *syst
 func (s *userImpl) GetUserByUsername(ctx context.Context, userName string) (user *model.LoginUserRes, err error) {
 	err = g.Try(func() {
 		user = &model.LoginUserRes{}
-		err = dao.SysUser.Ctx(ctx).Fields(user).Where("a=12").Where(dao.SysUser.Columns().UserName, userName).Scan(user)
-		liberr.ErrIsNil(ctx, err, "获取用户信息失败")
+		err = dao.SysUser.Ctx(ctx).Fields(user).Where(dao.SysUser.Columns().UserName, userName).Scan(user)
+		liberr.ErrIsNil(ctx, err, "账号密码错误")
+	})
+	return
+}
+
+// LoginLog 记录登录日志
+func (s *userImpl) LoginLog(ctx context.Context, params *model.LoginLogParams) {
+	ua := user_agent.New(params.UserAgent)
+	browser, _ := ua.Browser()
+	loginData := &do.SysLoginLog{
+		LoginName:     params.Username,
+		Ipaddr:        params.Ip,
+		LoginLocation: libUtils.GetCityByIp(params.Ip),
+		Browser:       browser,
+		Os:            ua.OS(),
+		Status:        params.Status,
+		Msg:           params.Msg,
+		LoginTime:     gtime.Now(),
+		Module:        params.Module,
+	}
+	_, err := dao.SysLoginLog.Ctx(ctx).Insert(loginData)
+	if err != nil {
+		g.Log().Error(ctx, err)
+	}
+}
+
+func (s *userImpl) UpdateLoginInfo(ctx context.Context, id uint64, ip string) (err error) {
+	g.Try(func() {
+		_, err = dao.SysUser.Ctx(ctx).WherePri(id).Update(g.Map{
+			dao.SysUser.Columns().LastLoginIp:   ip,
+			dao.SysUser.Columns().LastLoginTime: gtime.Now(),
+		})
+		liberr.ErrIsNil(ctx, err, "更新用户登录信息失败")
 	})
 	return
 }

+ 36 - 0
internal/app/system/service/token.go

@@ -0,0 +1,36 @@
+/*
+* @desc:后台token处理
+* @company:云南奇讯科技有限公司
+* @Author: yixiaohu
+* @Date:   2022/3/8 17:10
+ */
+
+package service
+
+import (
+	"context"
+	"github.com/gogf/gf/v2/frame/g"
+	"github.com/tiger1103/gfast/v3/internal/app/common/model"
+	commonService "github.com/tiger1103/gfast/v3/internal/app/common/service"
+	"github.com/tiger1103/gfast/v3/library/liberr"
+	"sync"
+)
+
+var (
+	options *model.TokenOptions
+	gT      commonService.IGfToken
+	lock    = &sync.Mutex{}
+)
+
+func GfToken(ctx context.Context) commonService.IGfToken {
+	if gT == nil {
+		lock.Lock()
+		defer lock.Unlock()
+		if gT == nil {
+			err := g.Cfg().MustGet(ctx, "gfToken").Struct(&options)
+			liberr.ErrIsNil(ctx, err)
+			gT = commonService.GfToken(options)
+		}
+	}
+	return gT
+}

+ 15 - 1
manifest/config/config.yaml

@@ -28,17 +28,31 @@ database:
   logger:
     level:   "all"
     stdout:  true
+    Path: "resource/log/sql"
 
   default:
     link:   "mysql:root:123456@tcp(127.0.0.1:3306)/gfast-v3"
     debug:  true
+    charset: "utf8mb4" #数据库编码
+    dryRun: false #空跑
+    maxIdle: 10 #连接池最大闲置的连接数
+    maxOpen: 10 #连接池最大打开的连接数
+    maxLifetime: 30 #(单位秒)连接对象可重复使用的时间长度
+
+gfToken:
+  cacheKey: "gfToken_"
+  timeOut: 10800
+  maxRefresh: 5400
+  multiLogin: true
+  encryptKey: "49c54195e750b04e74a8429b17896586"
+  cacheModel: "redis"
 
 # CLI.
 gfcli:
   gen:
     dao:
       - link:            "mysql:root:123456@tcp(127.0.0.1:3306)/gfast-v3"
-        tables:          "sys_user"
+        tables:          "sys_login_log"
         removePrefix:    "gf_"
         descriptionTag:  true
         noModelComment:  true