package model import ( "crypto/rand" "fmt" "math/big" "golang.org/x/crypto/bcrypt" "gorm.io/gorm" ) // 定义随机字符集 const passwordChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" // Yggdrasil ygg密码与用户id绑定 // @Description Yggdrasil认证密码数据模型 type Yggdrasil struct { ID int64 `gorm:"column:id;primaryKey;not null" json:"id"` Password string `gorm:"column:password;type:varchar(255);not null" json:"-"` // 加密后的密码,不返回给前端 // 关联 - Yggdrasil的ID引用User的ID,但不自动创建外键约束(避免循环依赖) User *User `gorm:"foreignKey:ID;references:ID;constraint:OnDelete:CASCADE,OnUpdate:CASCADE" json:"user,omitempty"` } func (Yggdrasil) TableName() string { return "yggdrasil" } // AfterCreate User创建后自动同步生成Yggdrasil密码记录 func (u *User) AfterCreate(tx *gorm.DB) error { // 生成随机明文密码 plainPassword := GenerateRandomPassword(16) // 使用 bcrypt 加密密码 hashedPassword, err := bcrypt.GenerateFromPassword([]byte(plainPassword), bcrypt.DefaultCost) if err != nil { return fmt.Errorf("密码加密失败: %w", err) } // 创建Yggdrasil记录(存储加密后的密码) ygg := Yggdrasil{ ID: u.ID, Password: string(hashedPassword), } if err := tx.Create(&ygg).Error; err != nil { return fmt.Errorf("同步生成Yggdrasil密码失败: %w", err) } return nil } // GenerateRandomPassword 生成指定长度的安全随机字符串 func GenerateRandomPassword(length int) string { b := make([]byte, length) for i := range b { num, err := rand.Int(rand.Reader, big.NewInt(int64(len(passwordChars)))) if err != nil { // 如果安全随机数生成失败,使用固定值(极端情况下的降级处理) b[i] = passwordChars[0] continue } b[i] = passwordChars[num.Int64()] } return string(b) }