package controllers import ( "encoding/json" "io" "strconv" "strings" "server/models" "server/pkg/jwtutil" authsvc "server/services/auth" beego "github.com/beego/beego/v2/server/web" ) // PlatformAuthConfigController 统一认证 —— 租户登录策略配置(平台端管理) // // 可配置:登录验证码方式、密码强度、会话时长、同账号最大在线设备数(1号1机) // 以及超限策略(踢掉旧会话 / 拒绝新登录)等。 type PlatformAuthConfigController struct { beego.Controller } func (c *PlatformAuthConfigController) serveJSON(data map[string]interface{}) { c.Data["json"] = data _ = c.ServeJSON() } // Prepare 统一鉴权:仅平台管理员可配置 func (c *PlatformAuthConfigController) Prepare() { authHeader := c.Ctx.Request.Header.Get("Authorization") if authHeader == "" { c.Ctx.Output.SetStatus(401) c.serveJSON(map[string]interface{}{"code": 401, "msg": "未登录"}) c.StopRun() return } parts := strings.SplitN(authHeader, " ", 2) if len(parts) != 2 || parts[0] != "Bearer" { c.Ctx.Output.SetStatus(401) c.serveJSON(map[string]interface{}{"code": 401, "msg": "认证信息格式错误"}) c.StopRun() return } claims, err := jwtutil.ParseToken(parts[1]) if err != nil { c.Ctx.Output.SetStatus(401) c.serveJSON(map[string]interface{}{"code": 401, "msg": "登录已失效,请重新登录"}) c.StopRun() return } if claims.UserType != "platform" { c.Ctx.Output.SetStatus(403) c.serveJSON(map[string]interface{}{"code": 403, "msg": "无权访问"}) c.StopRun() return } } // Detail 查询租户登录策略;未配置时返回系统默认值 // GET /platform/authConfig/detail?tid= func (c *PlatformAuthConfigController) Detail() { tid, _ := c.GetInt64("tid", 0) if tid == 0 { c.serveJSON(map[string]interface{}{"code": 400, "msg": "tid 不能为空"}) return } var cfg models.AuthTenantAuthConfig err := models.Orm.QueryTable(new(models.AuthTenantAuthConfig)). Filter("tid", tid).One(&cfg) if err != nil { // 未配置:返回默认值 policy := authsvc.GetTenantSessionPolicy(uint64(tid)) c.serveJSON(map[string]interface{}{ "code": 200, "msg": "success", "data": map[string]interface{}{ "tid": tid, "verify_type": "captcha", "open_verify": 1, "pwd_min_len": 8, "pwd_complexity": 0, "session_ttl": policy.SessionTTL, "max_session": policy.MaxSession, "kick_strategy": policy.KickStrategy, "mfa_required": 0, "ip_whitelist": "", "allow_third": "", "configured": false, }, }) return } ipList := "" if cfg.IPWhitelist != nil { ipList = *cfg.IPWhitelist } allowThird := "" if cfg.AllowThird != nil { allowThird = *cfg.AllowThird } c.serveJSON(map[string]interface{}{ "code": 200, "msg": "success", "data": map[string]interface{}{ "tid": cfg.Tid, "verify_type": cfg.VerifyType, "open_verify": cfg.OpenVerify, "pwd_min_len": cfg.PwdMinLen, "pwd_complexity": cfg.PwdComplexity, "session_ttl": cfg.SessionTTL, "max_session": cfg.MaxSession, "kick_strategy": cfg.KickStrategy, "mfa_required": cfg.MfaRequired, "ip_whitelist": ipList, "allow_third": allowThird, "configured": true, }, }) } // Save 保存租户登录策略(不存在则创建) // POST /platform/authConfig/save func (c *PlatformAuthConfigController) Save() { var p struct { Tid uint64 `json:"tid"` VerifyType string `json:"verify_type"` OpenVerify *int8 `json:"open_verify"` PwdMinLen int `json:"pwd_min_len"` PwdComplexity int8 `json:"pwd_complexity"` SessionTTL int `json:"session_ttl"` MaxSession int `json:"max_session"` KickStrategy int8 `json:"kick_strategy"` MfaRequired int8 `json:"mfa_required"` IPWhitelist string `json:"ip_whitelist"` AllowThird string `json:"allow_third"` } raw, _ := io.ReadAll(c.Ctx.Request.Body) if len(raw) > 0 { if err := json.Unmarshal(raw, &p); err != nil { c.serveJSON(map[string]interface{}{"code": 400, "msg": "参数错误"}) return } } if p.Tid == 0 { c.serveJSON(map[string]interface{}{"code": 400, "msg": "tid 不能为空"}) return } // 校验 IP 白名单为合法 JSON 数组 ip := strings.TrimSpace(p.IPWhitelist) if ip != "" { var arr []string if err := json.Unmarshal([]byte(ip), &arr); err != nil { c.serveJSON(map[string]interface{}{"code": 400, "msg": "IP白名单必须是 JSON 数组,如 [\"1.2.3.4\"]"}) return } } cfg := models.AuthTenantAuthConfig{ Tid: p.Tid, VerifyType: defaultStrAuth2(p.VerifyType, "captcha"), OpenVerify: 1, PwdMinLen: p.PwdMinLen, PwdComplexity: p.PwdComplexity, SessionTTL: p.SessionTTL, MaxSession: p.MaxSession, KickStrategy: p.KickStrategy, MfaRequired: p.MfaRequired, } if p.OpenVerify != nil { cfg.OpenVerify = *p.OpenVerify } if cfg.PwdMinLen <= 0 { cfg.PwdMinLen = 8 } if cfg.SessionTTL <= 0 { cfg.SessionTTL = authsvc.DefaultSessionTTL } if cfg.MaxSession <= 0 { cfg.MaxSession = authsvc.DefaultMaxSession } if cfg.KickStrategy != models.KickStrategyReject { cfg.KickStrategy = models.KickStrategyKickOld } if ip != "" { cfg.IPWhitelist = &ip } if v := strings.TrimSpace(p.AllowThird); v != "" { cfg.AllowThird = &v } exist := models.Orm.QueryTable(new(models.AuthTenantAuthConfig)).Filter("tid", p.Tid).Exist() if exist { fields := []string{"verify_type", "open_verify", "pwd_min_len", "pwd_complexity", "session_ttl", "max_session", "kick_strategy", "mfa_required"} if cfg.IPWhitelist != nil { fields = append(fields, "ip_whitelist") } if cfg.AllowThird != nil { fields = append(fields, "allow_third") } if _, err := models.Orm.Update(&cfg, fields...); err != nil { c.serveJSON(map[string]interface{}{"code": 500, "msg": "保存失败: " + err.Error()}) return } } else { if _, err := models.Orm.Insert(&cfg); err != nil { c.serveJSON(map[string]interface{}{"code": 500, "msg": "保存失败: " + err.Error()}) return } } c.serveJSON(map[string]interface{}{"code": 200, "msg": "success"}) } // Reset 恢复默认值(删除自定义配置) // DELETE /platform/authConfig/reset/:tid func (c *PlatformAuthConfigController) Reset() { tid, err := strconv.ParseUint(c.Ctx.Input.Param(":tid"), 10, 64) if err != nil || tid == 0 { c.serveJSON(map[string]interface{}{"code": 400, "msg": "无效 tid"}) return } if _, err := models.Orm.QueryTable(new(models.AuthTenantAuthConfig)). Filter("tid", tid).Delete(); err != nil { c.serveJSON(map[string]interface{}{"code": 500, "msg": "重置失败: " + err.Error()}) return } c.serveJSON(map[string]interface{}{"code": 200, "msg": "已恢复默认"}) } func defaultStrAuth2(v, def string) string { if strings.TrimSpace(v) == "" { return def } return strings.TrimSpace(v) }