做统一认证登录
This commit is contained in:
@@ -0,0 +1,219 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
||||
"server/models"
|
||||
"server/pkg/passwordutil"
|
||||
)
|
||||
|
||||
// SyncTenantUserInput 老表 → 认证中心同步入参
|
||||
type SyncTenantUserInput struct {
|
||||
Tid uint64
|
||||
Account string // 企业内账号
|
||||
Name string
|
||||
Phone string
|
||||
Email string
|
||||
PasswordHash string // 已哈希的密码;为空表示不修改密码
|
||||
GroupID uint64
|
||||
OrgID uint64
|
||||
Status int8
|
||||
IsDefault int8
|
||||
}
|
||||
|
||||
// SyncTenantUser 把租户用户同步到统一认证中心(幂等,可重复调用)。
|
||||
//
|
||||
// 归并规则与迁移脚本一致:手机号 > 邮箱 > 企业内账号。
|
||||
// - 身份不存在 → 创建;已存在 → 更新手机/邮箱/昵称/状态
|
||||
// - 绑定不存在 → 创建;已存在 → 更新账号/姓名/部门/角色/状态
|
||||
//
|
||||
// 注意:密码属于身份层(一人一份),因此在任一企业修改密码,
|
||||
// 该用户在其他企业的登录密码会同步变化——这是统一认证的预期行为。
|
||||
func SyncTenantUser(in SyncTenantUserInput) error {
|
||||
identity, err := findOrCreateIdentity(in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return upsertTenantBinding(identity, in)
|
||||
}
|
||||
|
||||
// RemoveTenantUser 删除该用户在指定企业的绑定(保留身份本身,
|
||||
// 因为该身份可能还绑定着其他企业)。
|
||||
func RemoveTenantUser(tid uint64, account, phone, email string) error {
|
||||
identity := findIdentityByKey(account, phone, email)
|
||||
if identity == nil {
|
||||
return nil // 认证中心无此身份,无需处理
|
||||
}
|
||||
_, err := models.Orm.QueryTable(new(models.AuthTenantUser)).
|
||||
Filter("tid", tid).
|
||||
Filter("identity_id", identity.ID).
|
||||
Delete()
|
||||
return err
|
||||
}
|
||||
|
||||
// findOrCreateIdentity 按归并键查找身份,不存在则创建
|
||||
func findOrCreateIdentity(in SyncTenantUserInput) (*models.AuthIdentity, error) {
|
||||
if m := findIdentityByKey(in.Account, in.Phone, in.Email); m != nil {
|
||||
// 补全手机/邮箱/昵称;密码仅在显式传入时更新
|
||||
update := map[string]interface{}{}
|
||||
if in.Phone != "" {
|
||||
update["mobile"] = in.Phone
|
||||
}
|
||||
if in.Email != "" {
|
||||
update["email"] = in.Email
|
||||
}
|
||||
if in.Name != "" {
|
||||
update["nickname"] = in.Name
|
||||
}
|
||||
if in.Status > 0 {
|
||||
update["status"] = in.Status
|
||||
}
|
||||
if in.PasswordHash != "" {
|
||||
update["password_hash"] = in.PasswordHash
|
||||
update["password_algo"] = passwordutil.AlgoOf(in.PasswordHash)
|
||||
}
|
||||
if len(update) > 0 {
|
||||
_, _ = models.Orm.QueryTable(new(models.AuthIdentity)).
|
||||
Filter("id", m.ID).
|
||||
Update(update)
|
||||
}
|
||||
// 重新读取,返回最新值
|
||||
_ = models.Orm.QueryTable(new(models.AuthIdentity)).Filter("id", m.ID).One(m)
|
||||
return m, nil
|
||||
}
|
||||
|
||||
item := &models.AuthIdentity{
|
||||
UnionID: strings.ReplaceAll(uuid.NewString(), "-", ""),
|
||||
PasswordAlgo: passwordutil.AlgoLegacy,
|
||||
Status: in.Status,
|
||||
}
|
||||
if in.Status == 0 {
|
||||
item.Status = models.AuthIdentityStatusEnabled
|
||||
}
|
||||
if in.Phone != "" {
|
||||
item.Mobile = &in.Phone
|
||||
}
|
||||
if in.Email != "" {
|
||||
item.Email = &in.Email
|
||||
}
|
||||
if in.Name != "" {
|
||||
item.Nickname = &in.Name
|
||||
}
|
||||
if in.PasswordHash != "" {
|
||||
item.PasswordHash = &in.PasswordHash
|
||||
item.PasswordAlgo = passwordutil.AlgoOf(in.PasswordHash)
|
||||
}
|
||||
id, err := models.Orm.Insert(item)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
item.ID = uint64(id)
|
||||
return item, nil
|
||||
}
|
||||
|
||||
// findIdentityByKey 按 手机 > 邮箱 > 账号 查找身份
|
||||
func findIdentityByKey(account, phone, email string) *models.AuthIdentity {
|
||||
base := models.Orm.QueryTable(new(models.AuthIdentity)).Filter("delete_time__isnull", true)
|
||||
|
||||
if v := strings.TrimSpace(phone); v != "" {
|
||||
m := &models.AuthIdentity{}
|
||||
if err := base.Filter("mobile", v).One(m); err == nil {
|
||||
return m
|
||||
}
|
||||
}
|
||||
if v := strings.TrimSpace(email); v != "" {
|
||||
m := &models.AuthIdentity{}
|
||||
if err := base.Filter("email", v).One(m); err == nil {
|
||||
return m
|
||||
}
|
||||
}
|
||||
// 企业内账号:先找绑定关系再回查身份
|
||||
if v := strings.TrimSpace(account); v != "" {
|
||||
var bind models.AuthTenantUser
|
||||
if err := models.Orm.QueryTable(new(models.AuthTenantUser)).
|
||||
Filter("account", v).
|
||||
Filter("delete_time__isnull", true).
|
||||
OrderBy("-is_default", "id").
|
||||
One(&bind); err == nil {
|
||||
m := &models.AuthIdentity{}
|
||||
if err := models.Orm.QueryTable(new(models.AuthIdentity)).
|
||||
Filter("id", bind.IdentityID).One(m); err == nil {
|
||||
return m
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// upsertTenantBinding 创建或更新身份-企业绑定
|
||||
func upsertTenantBinding(identity *models.AuthIdentity, in SyncTenantUserInput) error {
|
||||
var existed models.AuthTenantUser
|
||||
err := models.Orm.QueryTable(new(models.AuthTenantUser)).
|
||||
Filter("tid", in.Tid).
|
||||
Filter("identity_id", identity.ID).
|
||||
One(&existed)
|
||||
|
||||
if err != nil {
|
||||
// 新建绑定
|
||||
item := &models.AuthTenantUser{
|
||||
Tid: in.Tid,
|
||||
IdentityID: identity.ID,
|
||||
GroupID: in.GroupID,
|
||||
OrgID: in.OrgID,
|
||||
Status: in.Status,
|
||||
IsDefault: in.IsDefault,
|
||||
}
|
||||
if in.Status == 0 {
|
||||
item.Status = 1
|
||||
}
|
||||
if in.Account != "" {
|
||||
item.Account = &in.Account
|
||||
}
|
||||
if in.Name != "" {
|
||||
item.Name = &in.Name
|
||||
}
|
||||
if in.Phone != "" {
|
||||
item.Phone = &in.Phone
|
||||
}
|
||||
if in.Email != "" {
|
||||
item.Email = &in.Email
|
||||
}
|
||||
_, err := models.Orm.Insert(item)
|
||||
return err
|
||||
}
|
||||
|
||||
update := map[string]interface{}{}
|
||||
if in.Account != "" {
|
||||
update["account"] = in.Account
|
||||
}
|
||||
if in.Name != "" {
|
||||
update["name"] = in.Name
|
||||
}
|
||||
if in.Phone != "" {
|
||||
update["phone"] = in.Phone
|
||||
}
|
||||
if in.Email != "" {
|
||||
update["email"] = in.Email
|
||||
}
|
||||
if in.GroupID > 0 {
|
||||
update["group_id"] = in.GroupID
|
||||
}
|
||||
if in.OrgID > 0 {
|
||||
update["org_id"] = in.OrgID
|
||||
}
|
||||
if in.Status > 0 {
|
||||
update["status"] = in.Status
|
||||
}
|
||||
if in.IsDefault >= 0 {
|
||||
update["is_default"] = in.IsDefault
|
||||
}
|
||||
if len(update) == 0 {
|
||||
return nil
|
||||
}
|
||||
_, err = models.Orm.QueryTable(new(models.AuthTenantUser)).
|
||||
Filter("id", existed.ID).
|
||||
Update(update)
|
||||
return err
|
||||
}
|
||||
Reference in New Issue
Block a user