twofa.go 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. package model
  2. import (
  3. "errors"
  4. "fmt"
  5. "one-api/common"
  6. "time"
  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" gorm:"default:false"`
  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" gorm:"default:false"`
  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. if err := DB.Unscoped().Where("user_id = ?", t.UserId).Delete(&TwoFABackupCode{}).Error; err != nil {
  89. return err
  90. }
  91. // 硬删除2FA记录
  92. return DB.Unscoped().Delete(t).Error
  93. }
  94. // ResetFailedAttempts 重置失败尝试次数
  95. func (t *TwoFA) ResetFailedAttempts() error {
  96. t.FailedAttempts = 0
  97. t.LockedUntil = nil
  98. return t.Update()
  99. }
  100. // IncrementFailedAttempts 增加失败尝试次数
  101. func (t *TwoFA) IncrementFailedAttempts() error {
  102. t.FailedAttempts++
  103. // 检查是否需要锁定
  104. if t.FailedAttempts >= common.MaxFailAttempts {
  105. lockUntil := time.Now().Add(time.Duration(common.LockoutDuration) * time.Second)
  106. t.LockedUntil = &lockUntil
  107. }
  108. return t.Update()
  109. }
  110. // IsLocked 检查账户是否被锁定
  111. func (t *TwoFA) IsLocked() bool {
  112. if t.LockedUntil == nil {
  113. return false
  114. }
  115. return time.Now().Before(*t.LockedUntil)
  116. }
  117. // CreateBackupCodes 创建备用码
  118. func CreateBackupCodes(userId int, codes []string) error {
  119. // 先删除现有的备用码
  120. if err := DB.Where("user_id = ?", userId).Delete(&TwoFABackupCode{}).Error; err != nil {
  121. return err
  122. }
  123. // 创建新的备用码记录
  124. for _, code := range codes {
  125. hashedCode, err := common.HashBackupCode(code)
  126. if err != nil {
  127. return err
  128. }
  129. backupCode := TwoFABackupCode{
  130. UserId: userId,
  131. CodeHash: hashedCode,
  132. IsUsed: false,
  133. }
  134. if err := DB.Create(&backupCode).Error; err != nil {
  135. return err
  136. }
  137. }
  138. return nil
  139. }
  140. // ValidateBackupCode 验证并使用备用码
  141. func ValidateBackupCode(userId int, code string) (bool, error) {
  142. if !common.ValidateBackupCode(code) {
  143. return false, errors.New("验证码或备用码不正确")
  144. }
  145. normalizedCode := common.NormalizeBackupCode(code)
  146. // 查找未使用的备用码
  147. var backupCodes []TwoFABackupCode
  148. if err := DB.Where("user_id = ? AND is_used = false", userId).Find(&backupCodes).Error; err != nil {
  149. return false, err
  150. }
  151. // 验证备用码
  152. for _, bc := range backupCodes {
  153. if common.ValidatePasswordAndHash(normalizedCode, bc.CodeHash) {
  154. // 标记为已使用
  155. now := time.Now()
  156. bc.IsUsed = true
  157. bc.UsedAt = &now
  158. if err := DB.Save(&bc).Error; err != nil {
  159. return false, err
  160. }
  161. return true, nil
  162. }
  163. }
  164. return false, nil
  165. }
  166. // GetUnusedBackupCodeCount 获取未使用的备用码数量
  167. func GetUnusedBackupCodeCount(userId int) (int, error) {
  168. var count int64
  169. err := DB.Model(&TwoFABackupCode{}).Where("user_id = ? AND is_used = false", userId).Count(&count).Error
  170. return int(count), err
  171. }
  172. // DisableTwoFA 禁用用户的2FA
  173. func DisableTwoFA(userId int) error {
  174. twoFA, err := GetTwoFAByUserId(userId)
  175. if err != nil {
  176. return err
  177. }
  178. if twoFA == nil {
  179. return errors.New("用户未启用2FA")
  180. }
  181. // 删除2FA设置和备用码
  182. return twoFA.Delete()
  183. }
  184. // EnableTwoFA 启用2FA
  185. func (t *TwoFA) Enable() error {
  186. t.IsEnabled = true
  187. t.FailedAttempts = 0
  188. t.LockedUntil = nil
  189. return t.Update()
  190. }
  191. // ValidateTOTPAndUpdateUsage 验证TOTP并更新使用记录
  192. func (t *TwoFA) ValidateTOTPAndUpdateUsage(code string) (bool, error) {
  193. // 检查是否被锁定
  194. if t.IsLocked() {
  195. return false, fmt.Errorf("账户已被锁定,请在%v后重试", t.LockedUntil.Format("2006-01-02 15:04:05"))
  196. }
  197. // 验证TOTP码
  198. if !common.ValidateTOTPCode(t.Secret, code) {
  199. // 增加失败次数
  200. if err := t.IncrementFailedAttempts(); err != nil {
  201. common.SysError("更新2FA失败次数失败: " + err.Error())
  202. }
  203. return false, nil
  204. }
  205. // 验证成功,重置失败次数并更新最后使用时间
  206. now := time.Now()
  207. t.FailedAttempts = 0
  208. t.LockedUntil = nil
  209. t.LastUsedAt = &now
  210. if err := t.Update(); err != nil {
  211. common.SysError("更新2FA使用记录失败: " + err.Error())
  212. }
  213. return true, nil
  214. }
  215. // ValidateBackupCodeAndUpdateUsage 验证备用码并更新使用记录
  216. func (t *TwoFA) ValidateBackupCodeAndUpdateUsage(code string) (bool, error) {
  217. // 检查是否被锁定
  218. if t.IsLocked() {
  219. return false, fmt.Errorf("账户已被锁定,请在%v后重试", t.LockedUntil.Format("2006-01-02 15:04:05"))
  220. }
  221. // 验证备用码
  222. valid, err := ValidateBackupCode(t.UserId, code)
  223. if err != nil {
  224. return false, err
  225. }
  226. if !valid {
  227. // 增加失败次数
  228. if err := t.IncrementFailedAttempts(); err != nil {
  229. common.SysError("更新2FA失败次数失败: " + err.Error())
  230. }
  231. return false, nil
  232. }
  233. // 验证成功,重置失败次数并更新最后使用时间
  234. now := time.Now()
  235. t.FailedAttempts = 0
  236. t.LockedUntil = nil
  237. t.LastUsedAt = &now
  238. if err := t.Update(); err != nil {
  239. common.SysError("更新2FA使用记录失败: " + err.Error())
  240. }
  241. return true, nil
  242. }
  243. // GetTwoFAStats 获取2FA统计信息(管理员使用)
  244. func GetTwoFAStats() (map[string]interface{}, error) {
  245. var totalUsers, enabledUsers int64
  246. // 总用户数
  247. if err := DB.Model(&User{}).Count(&totalUsers).Error; err != nil {
  248. return nil, err
  249. }
  250. // 启用2FA的用户数
  251. if err := DB.Model(&TwoFA{}).Where("is_enabled = true").Count(&enabledUsers).Error; err != nil {
  252. return nil, err
  253. }
  254. enabledRate := float64(0)
  255. if totalUsers > 0 {
  256. enabledRate = float64(enabledUsers) / float64(totalUsers) * 100
  257. }
  258. return map[string]interface{}{
  259. "total_users": totalUsers,
  260. "enabled_users": enabledUsers,
  261. "enabled_rate": fmt.Sprintf("%.1f%%", enabledRate),
  262. }, nil
  263. }