twofa.go 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. package model
  2. import (
  3. "errors"
  4. "fmt"
  5. "time"
  6. "github.com/QuantumNous/new-api/common"
  7. "gorm.io/gorm"
  8. )
  9. // TwoFA 用户2FA设置表
  10. type TwoFA struct {
  11. Id int `json:"id" gorm:"primaryKey"`
  12. UserId int `json:"user_id" gorm:"unique;not null;index"`
  13. Secret string `json:"-" gorm:"type:varchar(255);not null"` // TOTP密钥,不返回给前端
  14. IsEnabled bool `json:"is_enabled"`
  15. FailedAttempts int `json:"failed_attempts" gorm:"default:0"`
  16. LockedUntil *time.Time `json:"locked_until,omitempty"`
  17. LastUsedAt *time.Time `json:"last_used_at,omitempty"`
  18. CreatedAt time.Time `json:"created_at"`
  19. UpdatedAt time.Time `json:"updated_at"`
  20. DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
  21. }
  22. // TwoFABackupCode 备用码使用记录表
  23. type TwoFABackupCode struct {
  24. Id int `json:"id" gorm:"primaryKey"`
  25. UserId int `json:"user_id" gorm:"not null;index"`
  26. CodeHash string `json:"-" gorm:"type:varchar(255);not null"` // 备用码哈希
  27. IsUsed bool `json:"is_used"`
  28. UsedAt *time.Time `json:"used_at,omitempty"`
  29. CreatedAt time.Time `json:"created_at"`
  30. DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
  31. }
  32. // GetTwoFAByUserId 根据用户ID获取2FA设置
  33. func GetTwoFAByUserId(userId int) (*TwoFA, error) {
  34. if userId == 0 {
  35. return nil, errors.New("用户ID不能为空")
  36. }
  37. var twoFA TwoFA
  38. err := DB.Where("user_id = ?", userId).First(&twoFA).Error
  39. if err != nil {
  40. if errors.Is(err, gorm.ErrRecordNotFound) {
  41. return nil, nil // 返回nil表示未设置2FA
  42. }
  43. return nil, err
  44. }
  45. return &twoFA, nil
  46. }
  47. // IsTwoFAEnabled 检查用户是否启用了2FA
  48. func IsTwoFAEnabled(userId int) bool {
  49. twoFA, err := GetTwoFAByUserId(userId)
  50. if err != nil || twoFA == nil {
  51. return false
  52. }
  53. return twoFA.IsEnabled
  54. }
  55. // CreateTwoFA 创建2FA设置
  56. func (t *TwoFA) Create() error {
  57. // 检查用户是否已存在2FA设置
  58. existing, err := GetTwoFAByUserId(t.UserId)
  59. if err != nil {
  60. return err
  61. }
  62. if existing != nil {
  63. return errors.New("用户已存在2FA设置")
  64. }
  65. // 验证用户存在
  66. var user User
  67. if err := DB.First(&user, t.UserId).Error; err != nil {
  68. if errors.Is(err, gorm.ErrRecordNotFound) {
  69. return errors.New("用户不存在")
  70. }
  71. return err
  72. }
  73. return DB.Create(t).Error
  74. }
  75. // Update 更新2FA设置
  76. func (t *TwoFA) Update() error {
  77. if t.Id == 0 {
  78. return errors.New("2FA记录ID不能为空")
  79. }
  80. return DB.Save(t).Error
  81. }
  82. // Delete 删除2FA设置
  83. func (t *TwoFA) Delete() error {
  84. if t.Id == 0 {
  85. return errors.New("2FA记录ID不能为空")
  86. }
  87. // 使用事务确保原子性
  88. return DB.Transaction(func(tx *gorm.DB) error {
  89. // 同时删除相关的备用码记录(硬删除)
  90. if err := tx.Unscoped().Where("user_id = ?", t.UserId).Delete(&TwoFABackupCode{}).Error; err != nil {
  91. return err
  92. }
  93. // 硬删除2FA记录
  94. return tx.Unscoped().Delete(t).Error
  95. })
  96. }
  97. // ResetFailedAttempts 重置失败尝试次数
  98. func (t *TwoFA) ResetFailedAttempts() error {
  99. t.FailedAttempts = 0
  100. t.LockedUntil = nil
  101. return t.Update()
  102. }
  103. // IncrementFailedAttempts 增加失败尝试次数
  104. func (t *TwoFA) IncrementFailedAttempts() error {
  105. t.FailedAttempts++
  106. // 检查是否需要锁定
  107. if t.FailedAttempts >= common.MaxFailAttempts {
  108. lockUntil := time.Now().Add(time.Duration(common.LockoutDuration) * time.Second)
  109. t.LockedUntil = &lockUntil
  110. }
  111. return t.Update()
  112. }
  113. // IsLocked 检查账户是否被锁定
  114. func (t *TwoFA) IsLocked() bool {
  115. if t.LockedUntil == nil {
  116. return false
  117. }
  118. return time.Now().Before(*t.LockedUntil)
  119. }
  120. // CreateBackupCodes 创建备用码
  121. func CreateBackupCodes(userId int, codes []string) error {
  122. return DB.Transaction(func(tx *gorm.DB) error {
  123. // 先删除现有的备用码
  124. if err := tx.Where("user_id = ?", userId).Delete(&TwoFABackupCode{}).Error; err != nil {
  125. return err
  126. }
  127. // 创建新的备用码记录
  128. for _, code := range codes {
  129. hashedCode, err := common.HashBackupCode(code)
  130. if err != nil {
  131. return err
  132. }
  133. backupCode := TwoFABackupCode{
  134. UserId: userId,
  135. CodeHash: hashedCode,
  136. IsUsed: false,
  137. }
  138. if err := tx.Create(&backupCode).Error; err != nil {
  139. return err
  140. }
  141. }
  142. return nil
  143. })
  144. }
  145. // ValidateBackupCode 验证并使用备用码
  146. func ValidateBackupCode(userId int, code string) (bool, error) {
  147. if !common.ValidateBackupCode(code) {
  148. return false, errors.New("验证码或备用码不正确")
  149. }
  150. normalizedCode := common.NormalizeBackupCode(code)
  151. // 查找未使用的备用码
  152. var backupCodes []TwoFABackupCode
  153. if err := DB.Where("user_id = ? AND is_used = false", userId).Find(&backupCodes).Error; err != nil {
  154. return false, err
  155. }
  156. // 验证备用码
  157. for _, bc := range backupCodes {
  158. if common.ValidatePasswordAndHash(normalizedCode, bc.CodeHash) {
  159. // 标记为已使用
  160. now := time.Now()
  161. bc.IsUsed = true
  162. bc.UsedAt = &now
  163. if err := DB.Save(&bc).Error; err != nil {
  164. return false, err
  165. }
  166. return true, nil
  167. }
  168. }
  169. return false, nil
  170. }
  171. // GetUnusedBackupCodeCount 获取未使用的备用码数量
  172. func GetUnusedBackupCodeCount(userId int) (int, error) {
  173. var count int64
  174. err := DB.Model(&TwoFABackupCode{}).Where("user_id = ? AND is_used = false", userId).Count(&count).Error
  175. return int(count), err
  176. }
  177. // DisableTwoFA 禁用用户的2FA
  178. func DisableTwoFA(userId int) error {
  179. twoFA, err := GetTwoFAByUserId(userId)
  180. if err != nil {
  181. return err
  182. }
  183. if twoFA == nil {
  184. return ErrTwoFANotEnabled
  185. }
  186. // 删除2FA设置和备用码
  187. return twoFA.Delete()
  188. }
  189. // EnableTwoFA 启用2FA
  190. func (t *TwoFA) Enable() error {
  191. t.IsEnabled = true
  192. t.FailedAttempts = 0
  193. t.LockedUntil = nil
  194. return t.Update()
  195. }
  196. // ValidateTOTPAndUpdateUsage 验证TOTP并更新使用记录
  197. func (t *TwoFA) ValidateTOTPAndUpdateUsage(code string) (bool, error) {
  198. // 检查是否被锁定
  199. if t.IsLocked() {
  200. return false, fmt.Errorf("账户已被锁定,请在%v后重试", t.LockedUntil.Format("2006-01-02 15:04:05"))
  201. }
  202. // 验证TOTP码
  203. if !common.ValidateTOTPCode(t.Secret, code) {
  204. // 增加失败次数
  205. if err := t.IncrementFailedAttempts(); err != nil {
  206. common.SysLog("更新2FA失败次数失败: " + err.Error())
  207. }
  208. return false, nil
  209. }
  210. // 验证成功,重置失败次数并更新最后使用时间
  211. now := time.Now()
  212. t.FailedAttempts = 0
  213. t.LockedUntil = nil
  214. t.LastUsedAt = &now
  215. if err := t.Update(); err != nil {
  216. common.SysLog("更新2FA使用记录失败: " + err.Error())
  217. }
  218. return true, nil
  219. }
  220. // ValidateBackupCodeAndUpdateUsage 验证备用码并更新使用记录
  221. func (t *TwoFA) ValidateBackupCodeAndUpdateUsage(code string) (bool, error) {
  222. // 检查是否被锁定
  223. if t.IsLocked() {
  224. return false, fmt.Errorf("账户已被锁定,请在%v后重试", t.LockedUntil.Format("2006-01-02 15:04:05"))
  225. }
  226. // 验证备用码
  227. valid, err := ValidateBackupCode(t.UserId, code)
  228. if err != nil {
  229. return false, err
  230. }
  231. if !valid {
  232. // 增加失败次数
  233. if err := t.IncrementFailedAttempts(); err != nil {
  234. common.SysLog("更新2FA失败次数失败: " + err.Error())
  235. }
  236. return false, nil
  237. }
  238. // 验证成功,重置失败次数并更新最后使用时间
  239. now := time.Now()
  240. t.FailedAttempts = 0
  241. t.LockedUntil = nil
  242. t.LastUsedAt = &now
  243. if err := t.Update(); err != nil {
  244. common.SysLog("更新2FA使用记录失败: " + err.Error())
  245. }
  246. return true, nil
  247. }
  248. // GetTwoFAStats 获取2FA统计信息(管理员使用)
  249. func GetTwoFAStats() (map[string]interface{}, error) {
  250. var totalUsers, enabledUsers int64
  251. // 总用户数
  252. if err := DB.Model(&User{}).Count(&totalUsers).Error; err != nil {
  253. return nil, err
  254. }
  255. // 启用2FA的用户数
  256. if err := DB.Model(&TwoFA{}).Where("is_enabled = true").Count(&enabledUsers).Error; err != nil {
  257. return nil, err
  258. }
  259. enabledRate := float64(0)
  260. if totalUsers > 0 {
  261. enabledRate = float64(enabledUsers) / float64(totalUsers) * 100
  262. }
  263. return map[string]interface{}{
  264. "total_users": totalUsers,
  265. "enabled_users": enabledUsers,
  266. "enabled_rate": fmt.Sprintf("%.1f%%", enabledRate),
  267. }, nil
  268. }