twofa.go 7.9 KB

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