Files
yunzerwebsiteallinone/go/controllers/platform_auth_config.go
T
2026-09-20 00:19:08 +08:00

241 lines
6.8 KiB
Go

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)
}