/* * @desc:登录 * @company:云南奇讯科技有限公司 * @Author: yixiaohu * @Date: 2022/4/27 21:52 */ package controller import ( "context" "strings" "github.com/gogf/gf/v2/crypto/gmd5" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/os/gctx" "github.com/gogf/gf/v2/util/gconv" "github.com/gogf/gf/v2/util/gmode" "github.com/tiger1103/gfast/v3/api/v1/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/geoallow" "github.com/tiger1103/gfast/v3/library/libUtils" ) var ( Login = loginController{} ) type loginController struct { BaseController } func (c *loginController) Login(ctx context.Context, req *system.UserLoginReq) (res *system.UserLoginRes, err error) { var ( user *model.LoginUserRes token string permissions []string menuList []*model.UserMenus ) // 登录地区 IP 闸门: 非中国大陆(非内部)地址一律禁止登录。先于验证码/密码校验执行, 失败不泄露账号信息。 ip := libUtils.GetRealClientIP(ctx) userAgent := libUtils.GetUserAgent(ctx) if err = loginCheckIPAllow(ctx, ip); err != nil { // 保存登录失败的日志信息 service.SysLoginLog().Invoke(gctx.New(), &model.LoginLogParams{ Status: 0, Username: req.Username, Ip: ip, UserAgent: userAgent, Msg: err.Error(), Module: "系统后台", }) return } //判断验证码是否正确 debug := gmode.IsDevelop() if !debug { if !commonService.Captcha().VerifyString(req.VerifyKey, req.VerifyCode) { err = gerror.New("验证码输入错误") return } } user, err = service.SysUser().GetAdminUserByUsernamePassword(ctx, req) if err != nil { // 保存登录失败的日志信息 service.SysLoginLog().Invoke(gctx.New(), &model.LoginLogParams{ Status: 0, Username: req.Username, Ip: ip, UserAgent: userAgent, Msg: err.Error(), Module: "系统后台", }) return } err = service.SysUser().UpdateLoginInfo(ctx, user.Id, ip) if err != nil { return } // 报存登录成功的日志信息 service.SysLoginLog().Invoke(gctx.New(), &model.LoginLogParams{ Status: 1, Username: req.Username, Ip: ip, UserAgent: userAgent, Msg: "登录成功", Module: "系统后台", }) key := gconv.String(user.Id) + "-" + gmd5.MustEncryptString(user.UserName) + gmd5.MustEncryptString(user.UserPassword) 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().GenerateToken(ctx, key, user) g.Log().Debugf(ctx, "==========================key:%v;user:%v;token:%v==========================", key, user, token) if err != nil { g.Log().Error(ctx, err) err = gerror.New("登录失败,后端服务出现错误") return } _, err = service.ModuleTenant().AddRedisByTenantId(ctx, &system.AddRedisByTenantIdReq{TenantId: user.TenantId}) if err != nil { return } //获取用户菜单数据 menuList, permissions, err = service.SysUser().GetAdminRules(ctx, user.Id) if err != nil { return } res = &system.UserLoginRes{ UserInfo: user, Token: token, MenuList: menuList, Permissions: permissions, } //用户在线状态保存 service.SysUserOnline().Invoke(gctx.New(), &model.SysUserOnlineParams{ UserAgent: userAgent, Uuid: gmd5.MustEncrypt(token), Token: token, Username: user.UserName, Ip: ip, }) return } // LoginOut 退出登录 func (c *loginController) LoginOut(ctx context.Context, req *system.UserLoginOutReq) (res *system.UserLoginOutRes, err error) { err = service.GfToken().RemoveToken(ctx, service.GfToken().GetRequestToken(g.RequestFromCtx(ctx))) return } // loginCheckIPAllow 登录地区 IP 闸门判定。 // // 配置位于 config.yaml 的 loginIpFilter 段: // - enabled: 总开关, 默认开启; 关闭则一律放行(紧急恢复手段) // - allowIps: 显式放行 IP 列表(不受地区限制), 如办公网/跳板出口公网 IP // - denyMsg: 拒绝时返回给前端的文案 // // 放行条件: 开关关闭 / 命中 allowIps / 内部·保留地址(环回、内网、链路本地等) / // 中国大陆分配网段。境外及港澳台地址返回拒绝错误。 func loginCheckIPAllow(ctx context.Context, ip string) error { if !g.Cfg().MustGet(ctx, "loginIpFilter.enabled", true).Bool() { return nil } for _, a := range g.Cfg().MustGet(ctx, "loginIpFilter.allowIps").Strings() { if strings.TrimSpace(a) == ip { return nil } } if geoallow.Allow(ip) { return nil } return gerror.New(g.Cfg().MustGet(ctx, "loginIpFilter.denyMsg", "当前网络环境不允许登录").String()) }