package controllers import ( "encoding/json" "io" "strings" "server/models" "server/pkg/jwtutil" "server/services" beego "github.com/beego/beego/v2/server/web" ) type backendAuthLoginRequest struct { TenantName string `json:"tenant_name"` Account string `json:"account"` Password string `json:"password"` Code string `json:"code"` // 极验4验证参数 CaptchaID string `json:"captcha_id"` LotNumber string `json:"lot_number"` PassToken string `json:"pass_token"` GenTime string `json:"gen_time"` CaptchaOutput string `json:"captcha_output"` } // BackendAuthController backend 端认证控制器 type BackendAuthController struct { beego.Controller } func (c *BackendAuthController) serveJSON(data map[string]interface{}) { c.Data["json"] = data _ = c.ServeJSON() } // recordLoginLog 记录登录日志(成功/失败均记录,失败不影响登录流程) func (c *BackendAuthController) recordLoginLog(tid *uint64, userID uint64, account, userName, tenantName, loginType string, status int8, message string) { ip := c.Ctx.Input.IP() userAgent := c.Ctx.Request.UserAgent() RecordLoginLog(tid, userID, account, userName, tenantName, loginType, status, message, ip, userAgent) } // LoginBackend backend 登录(需要租户) func (c *BackendAuthController) LoginBackend() { var req backendAuthLoginRequest body := c.Ctx.Input.RequestBody if len(body) == 0 { var err error body, err = io.ReadAll(c.Ctx.Request.Body) if err != nil { c.serveJSON(map[string]interface{}{"code": 400, "msg": "参数错误"}) return } } if len(body) == 0 { c.serveJSON(map[string]interface{}{"code": 400, "msg": "参数错误"}) return } if err := json.Unmarshal(body, &req); err != nil { c.serveJSON(map[string]interface{}{"code": 400, "msg": "参数错误"}) return } req.TenantName = strings.TrimSpace(req.TenantName) req.Account = strings.TrimSpace(req.Account) req.Password = strings.TrimSpace(req.Password) if req.TenantName == "" || req.Account == "" || req.Password == "" { c.serveJSON(map[string]interface{}{"code": 400, "msg": "租户名称、用户名或密码不能为空"}) return } cfg, _ := models.GetPlatformLoginVerify() if cfg.OpenVerifyEnabled == 1 { if cfg.VerifyType == "geetest4" || cfg.VerifyType == "geetest" { if req.LotNumber == "" || req.PassToken == "" || req.GenTime == "" || req.CaptchaOutput == "" { c.serveJSON(map[string]interface{}{"code": 400, "msg": "请完成人机验证"}) return } // TODO: 集成极验4服务端 SDK 后在这里进行二次校验 } else if cfg.VerifyType == "geetest3" { if req.CaptchaOutput == "" { c.serveJSON(map[string]interface{}{"code": 400, "msg": "请完成人机验证"}) return } // TODO: 集成极验3服务端 SDK 后在这里进行二次校验 } else if cfg.VerifyType == "sms" || cfg.VerifyType == "email" { if strings.TrimSpace(req.Code) == "" { c.serveJSON(map[string]interface{}{"code": 400, "msg": "请输入验证码"}) return } if err := services.VerifyBackendLoginCode(req.TenantName, req.Account, cfg.VerifyType, req.Code); err != nil { c.serveJSON(map[string]interface{}{"code": 400, "msg": err.Error()}) return } } } token, loginUser, err := services.BackendLogin(req.TenantName, req.Account, req.Password) if err != nil { c.recordLoginLog(nil, 0, req.Account, "", req.TenantName, "password", 0, err.Error()) c.serveJSON(map[string]interface{}{"code": 401, "msg": err.Error()}) return } tid := loginUser.Tid c.recordLoginLog(&tid, loginUser.ID, loginUser.Account, loginUser.Name, req.TenantName, "password", 1, "登录成功") c.serveJSON(map[string]interface{}{ "code": 200, "msg": "登录成功", "data": map[string]interface{}{ "token": token, "user": map[string]interface{}{ "id": loginUser.ID, "account": loginUser.Account, "name": loginUser.Name, "tid": loginUser.Tid, "tenant_name": loginUser.TenantName, "rid": loginUser.Rid, "avatar": loginUser.Avatar, "role_name": loginUser.RoleName, }, }, }) } // GetCurrentUser 当前登录 backend 用户信息,需 Bearer Token func (c *BackendAuthController) GetCurrentUser() { authHeader := c.Ctx.Request.Header.Get("Authorization") if authHeader == "" { c.serveJSON(map[string]interface{}{"code": 401, "msg": "未登录"}) return } authParts := strings.SplitN(authHeader, " ", 2) if len(authParts) != 2 || authParts[0] != "Bearer" { c.serveJSON(map[string]interface{}{"code": 401, "msg": "认证信息格式错误"}) return } claims, err := jwtutil.ParseToken(authParts[1]) if err != nil { c.serveJSON(map[string]interface{}{"code": 401, "msg": "无效的token"}) return } if claims.UserType != "backend" && claims.UserType != "app" { c.serveJSON(map[string]interface{}{"code": 403, "msg": "无权访问"}) return } var tenantUser models.SystemTenantUser err = models.Orm.QueryTable(new(models.SystemTenantUser)). Filter("uid", claims.UserID). Filter("tid", claims.TenantId). One(&tenantUser) if err != nil { c.serveJSON(map[string]interface{}{"code": 401, "msg": "用户不存在"}) return } if tenantUser.Status == 0 { c.serveJSON(map[string]interface{}{"code": 401, "msg": "账号已禁用"}) return } account := "" if tenantUser.Account != nil { account = strings.TrimSpace(*tenantUser.Account) } name := "" if tenantUser.Name != nil { name = strings.TrimSpace(*tenantUser.Name) } // 查询当前租户名称 tenantName := "" var tenant models.SystemTenant if err := models.Orm.QueryTable(new(models.SystemTenant)).Filter("id", claims.TenantId).One(&tenant); err == nil { tenantName = strings.TrimSpace(tenant.TenantName) } c.serveJSON(map[string]interface{}{ "code": 200, "msg": "success", "data": map[string]interface{}{ "id": tenantUser.Uid, "account": account, "name": name, "tid": tenantUser.Tid, "tenant_name": tenantName, "rid": 0, "avatar": "", "role_name": "", }, }) } // GetCurrentUserProfile 当前登录用户的角色 / 部门 / 职位 // GET /backend/getCurrentUserProfile // 返回 department / position(来自员工档案,按登录账号 + 租户匹配); // 若均无,则回退到角色名(role_name)。 func (c *BackendAuthController) GetCurrentUserProfile() { authHeader := c.Ctx.Request.Header.Get("Authorization") if authHeader == "" { c.serveJSON(map[string]interface{}{"code": 401, "msg": "未登录"}) return } authParts := strings.SplitN(authHeader, " ", 2) if len(authParts) != 2 || authParts[0] != "Bearer" { c.serveJSON(map[string]interface{}{"code": 401, "msg": "认证信息格式错误"}) return } claims, err := jwtutil.ParseToken(authParts[1]) if err != nil { c.serveJSON(map[string]interface{}{"code": 401, "msg": "无效的token"}) return } if claims.UserType != "backend" && claims.UserType != "app" { c.serveJSON(map[string]interface{}{"code": 403, "msg": "无权访问"}) return } var tenantUser models.SystemTenantUser if err := models.Orm.QueryTable(new(models.SystemTenantUser)). Filter("uid", claims.UserID). Filter("tid", claims.TenantId). One(&tenantUser); err != nil { c.serveJSON(map[string]interface{}{"code": 401, "msg": "用户不存在"}) return } account := "" if tenantUser.Account != nil { account = strings.TrimSpace(*tenantUser.Account) } name := "" if tenantUser.Name != nil { name = strings.TrimSpace(*tenantUser.Name) } // 角色名:按 group_id 查询本租户下的租户角色(cid=2) roleName := "" if tenantUser.GroupID > 0 { var role models.AdminRole if err := models.Orm.QueryTable(new(models.AdminRole)). Filter("id", tenantUser.GroupID). Filter("cid", 2). Filter("tenant_id", claims.TenantId). One(&role); err == nil { roleName = role.Name } } // 部门 / 职位:来自员工档案,按账号 + 租户匹配 department := "" position := "" if account != "" { var emp models.BackendEmployee qs := models.Orm.QueryTable(new(models.BackendEmployee)).Filter("account", account) if claims.TenantId > 0 { qs = qs.Filter("tid", int(claims.TenantId)) } if err := qs.One(&emp); err == nil { if emp.Department != nil { department = strings.TrimSpace(*emp.Department) } if emp.Position != nil { position = strings.TrimSpace(*emp.Position) } } } // 员工档案的 department 字段存的是组织ID,需转换为组织名称展示 if department != "" { orgNames := (&BackendEmployeeFileController{}).efOrgNameMap(int(claims.TenantId)) department = efOrgNameByID(orgNames, department) } c.serveJSON(map[string]interface{}{ "code": 200, "msg": "success", "data": map[string]interface{}{ "account": account, "name": name, "role_name": roleName, "department": department, "position": position, }, }) } // SendLoginCode 发送 backend 登录验证码 func (c *BackendAuthController) SendLoginCode() { var req struct { Account string `json:"account"` TenantName string `json:"tenant_name"` Channel string `json:"channel"` } body := c.Ctx.Input.RequestBody if len(body) == 0 { var err error body, err = io.ReadAll(c.Ctx.Request.Body) if err != nil { c.serveJSON(map[string]interface{}{"code": 400, "msg": "参数错误"}) return } } if err := json.Unmarshal(body, &req); err != nil { c.serveJSON(map[string]interface{}{"code": 400, "msg": "参数错误"}) return } cfg, _ := models.GetPlatformLoginVerify() if cfg.OpenVerifyEnabled != 1 { c.serveJSON(map[string]interface{}{"code": 400, "msg": "当前未开启验证"}) return } channel := strings.TrimSpace(req.Channel) if channel == "" { channel = cfg.VerifyType } if channel != "sms" && channel != "email" { c.serveJSON(map[string]interface{}{"code": 400, "msg": "仅支持短信/邮箱验证码"}) return } if err := services.SendBackendLoginCode(req.TenantName, req.Account, channel); err != nil { c.serveJSON(map[string]interface{}{"code": 400, "msg": err.Error()}) return } c.serveJSON(map[string]interface{}{"code": 200, "msg": "验证码已发送"}) } // LoginBySms 手机号验证码登录(占位实现) func (c *BackendAuthController) LoginBySms() { c.serveJSON(map[string]interface{}{ "code": 501, "msg": "手机号验证码登录暂未实现", }) } // Logout backend 退出登录(当前为无状态直接返回成功) func (c *BackendAuthController) Logout() { c.serveJSON(map[string]interface{}{ "code": 200, "msg": "退出成功", }) } // GetGeetest3Infos 获取 backend 极验3.0配置 func (c *BackendAuthController) GetGeetest3Infos() { cfg, _ := models.GetPlatformLoginVerify() if cfg.Geetest3ID == nil || cfg.Geetest3Key == nil { c.serveJSON(map[string]interface{}{"code": 404, "msg": "未配置极验3参数"}) return } c.serveJSON(map[string]interface{}{ "code": 200, "msg": "success", "data": map[string]interface{}{ "captcha_id": *cfg.Geetest3ID, "captcha_key": *cfg.Geetest3Key, }, }) } // GetGeetest4Infos 获取 backend 极验4.0配置 func (c *BackendAuthController) GetGeetest4Infos() { cfg, _ := models.GetPlatformLoginVerify() if cfg.Geetest4ID == nil || cfg.Geetest4Key == nil { c.serveJSON(map[string]interface{}{"code": 404, "msg": "未配置极验4参数"}) return } c.serveJSON(map[string]interface{}{ "code": 200, "msg": "success", "data": map[string]interface{}{ "captcha_id": *cfg.Geetest4ID, "captcha_key": *cfg.Geetest4Key, }, }) } // GetOpenVerify 判断是否开启 backend 登录验证 func (c *BackendAuthController) GetOpenVerify() { cfg, _ := models.GetPlatformLoginVerify() openVerify := "0" if cfg.OpenVerifyEnabled == 1 { openVerify = "1" } c.serveJSON(map[string]interface{}{ "code": 200, "msg": "ok", "data": []map[string]string{ { "label": "openVerify", "value": openVerify, }, { "label": "verifyType", "value": cfg.VerifyType, }, }, }) } // Register 注册(占位实现) func (c *BackendAuthController) Register() { c.serveJSON(map[string]interface{}{ "code": 501, "msg": "注册暂未实现", }) } // SendRegisterCode 发送注册验证码(占位实现) func (c *BackendAuthController) SendRegisterCode() { c.serveJSON(map[string]interface{}{ "code": 501, "msg": "发送注册验证码暂未实现", }) } // ResetPassword 忘记密码重置(占位实现) func (c *BackendAuthController) ResetPassword() { c.serveJSON(map[string]interface{}{ "code": 501, "msg": "重置密码暂未实现", }) } // SendResetCode 发送找回密码验证码(占位实现) func (c *BackendAuthController) SendResetCode() { c.serveJSON(map[string]interface{}{ "code": 501, "msg": "发送找回密码验证码暂未实现", }) }