twofa.go 8.1 KB

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