- 后端:新增 refresh_token 模型与服务,登录签发 refresh token,支持静默续期与登出撤销 (backend/internal/model/refresh_token.go, backend/internal/service/auth/refresh.go, auth_controller/auth_service/dto/router/jwt 配套改动) - 前端:登录页改为「记住登录」(只持久化引导令牌与用户名,不落盘密码),401 自动刷新并重试原请求 (stores/user.js, api/request.js, api/auth.js, main.js, Login.vue 配套改动) - 部署:已在容器中验证运行(镜像构建于 2026-11-03 00:50,服务 healthy) - 安全:仓库内无任何 .env/证书/密钥文件变更
This commit is contained in:
1 parent
4bf7fb6e5c
commit
a01b89f942
13 files changed
+531
-208
No files matched your search
@@ -29,9 +29,26 @@ func NewAuthService(db *gorm.DB, cfg *config.Config) *AuthService {
|
||||
return &AuthService{db: db, cfg: cfg}
|
||||
}
|
||||
|
||||
// LoginResult 登录/刷新成功后的令牌与用户信息(供 controller 组装响应) // Logan.Li
|
||||
type LoginResult struct {
|
||||
AccessToken string
|
||||
RefreshToken string
|
||||
RefreshExpiresAt time.Time
|
||||
User *model.User
|
||||
}
|
||||
|
||||
// issueAccessToken 签发访问令牌(HS256,24h) // Logan.Li
|
||||
func (s *AuthService) issueAccessToken(user *model.User) (string, error) {
|
||||
token, err := utils.GenerateToken(user.ID, user.Username, user.Role, s.cfg.JWTSecret)
|
||||
if err != nil {
|
||||
return "", errors.New("令牌生成失败")
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
|
||||
// Login 校验账号密码并签发 JWT。
|
||||
// 含账号级防护:连续失败达上限后锁定一段时间;成功登录清零计数。 // Logan.Li
|
||||
func (s *AuthService) Login(req dto.LoginRequest) (*dto.LoginResponse, error) {
|
||||
func (s *AuthService) Login(req dto.LoginRequest, ip, userAgent string) (*LoginResult, error) {
|
||||
var user model.User
|
||||
if err := s.db.Where("username = ?", req.Username).First(&user).Error; err != nil {
|
||||
return nil, errors.New("用户名或密码错误")
|
||||
@@ -64,21 +81,21 @@ func (s *AuthService) Login(req dto.LoginRequest) (*dto.LoginResponse, error) {
|
||||
now := time.Now()
|
||||
s.db.Model(&user).Update("last_login", now)
|
||||
|
||||
// 签发 JWT // Logan.Li
|
||||
token, err := utils.GenerateToken(user.ID, user.Username, user.Role, s.cfg.JWTSecret)
|
||||
// 签发访问令牌 + 刷新令牌(支撑「记住登录」,密码不落盘) // Logan.Li
|
||||
accessToken, err := s.issueAccessToken(&user)
|
||||
if err != nil {
|
||||
return nil, errors.New("令牌生成失败")
|
||||
return nil, err
|
||||
}
|
||||
refreshToken, refreshExpiresAt, err := s.IssueRefreshToken(user.ID, ip, userAgent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &dto.LoginResponse{
|
||||
Token: token,
|
||||
UserInfo: dto.UserInfoResponse{
|
||||
ID: user.ID,
|
||||
Username: user.Username,
|
||||
Email: user.Email,
|
||||
Avatar: user.Avatar,
|
||||
Role: user.Role,
|
||||
},
|
||||
return &LoginResult{
|
||||
AccessToken: accessToken,
|
||||
RefreshToken: refreshToken,
|
||||
RefreshExpiresAt: refreshExpiresAt,
|
||||
User: &user,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -125,5 +142,10 @@ func (s *AuthService) ChangePassword(userID uint, req dto.ChangePasswordRequest)
|
||||
user.TokenValidAfter = time.Now() // 旧 JWT 全部失效,需重新登录 // Logan.Li
|
||||
user.FailedLoginAttempts = 0
|
||||
user.LockedUntil = nil
|
||||
return s.db.Save(&user).Error
|
||||
if err := s.db.Save(&user).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
// 刷新令牌一并撤销,否则「记住登录」会绕过改密失效基线 // Logan.Li
|
||||
s.RevokeAllForUser(user.ID)
|
||||
return nil
|
||||
}
|
||||
Reference in new issue
Block a user