241 lines
6.8 KiB
Go
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)
|
|
}
|