mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-07 16:16:37 +08:00
fix(user): revoke all sessions and access tokens on password change
- Store user password hash in session during login - Validate password hash compatibility on requests to prevent session reuse - Revoke all user access tokens and clear session on ChangePassword
This commit is contained in:
@@ -17,6 +17,7 @@ const (
|
||||
TokenAuthKey = "token_auth" // 标记当前请求是否通过 Access Token 鉴权
|
||||
TokenAdminKey = "token_admin" // Access Token 本身是否具有管理员权限
|
||||
SessionTokenKey = "oauth_session_token" //nolint:gosec // false positive: this is a session key, not hardcoded credentials
|
||||
PasswordHashKey = "password_hash"
|
||||
)
|
||||
|
||||
// OAuth State 缓存 Key 格式与过期时间
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/Rain-kl/Wavelet/internal/model"
|
||||
"github.com/Rain-kl/Wavelet/internal/otel_trace"
|
||||
"github.com/Rain-kl/Wavelet/internal/util"
|
||||
"github.com/gin-contrib/sessions"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@@ -41,39 +42,44 @@ func GetUserFromRequest(c *gin.Context) (*model.User, error) {
|
||||
}
|
||||
|
||||
var user model.User
|
||||
var authenticated bool
|
||||
var tokenAuth bool
|
||||
var tokenAdmin bool
|
||||
|
||||
// 优先使用 Access Token 鉴权
|
||||
if tokenStr != "" {
|
||||
tokenHash := model.HashToken(tokenStr)
|
||||
var tokenRecord model.AccessToken
|
||||
if err := db.DB(ctx).Where("token_hash = ?", tokenHash).First(&tokenRecord).Error; err == nil {
|
||||
if err := db.DB(ctx).Where("id = ? AND is_active = ?", tokenRecord.UserID, true).First(&user).Error; err == nil {
|
||||
authenticated = true
|
||||
tokenAuth = true
|
||||
tokenAdmin = tokenRecord.IsAdmin
|
||||
util.SetToContext(c, TokenAuthKey, true)
|
||||
util.SetToContext(c, TokenAdminKey, tokenRecord.IsAdmin)
|
||||
return &user, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !authenticated {
|
||||
// load user from session
|
||||
userID := GetUserIDFromContext(c)
|
||||
if userID <= 0 {
|
||||
return nil, errors.New("unauthorized")
|
||||
}
|
||||
// 降级使用 Session 鉴权
|
||||
userID := GetUserIDFromContext(c)
|
||||
if userID <= 0 {
|
||||
return nil, errors.New("unauthorized")
|
||||
}
|
||||
|
||||
// load user from db to make sure is active
|
||||
tx := db.DB(ctx).Where("id = ? AND is_active = ?", userID, true).First(&user)
|
||||
if tx.Error != nil {
|
||||
return nil, tx.Error
|
||||
// load user from db to make sure is active
|
||||
tx := db.DB(ctx).Where("id = ? AND is_active = ?", userID, true).First(&user)
|
||||
if tx.Error != nil {
|
||||
return nil, tx.Error
|
||||
}
|
||||
|
||||
// 密码哈希校验:当用户存在本地密码时,要求 Session 中的密码哈希必须与当前数据库中一致
|
||||
if user.Password != "" {
|
||||
session := sessions.Default(c)
|
||||
sessionHash, _ := session.Get(PasswordHashKey).(string)
|
||||
if sessionHash != user.Password {
|
||||
return nil, errors.New("session expired due to password change")
|
||||
}
|
||||
}
|
||||
|
||||
// set keys in context
|
||||
util.SetToContext(c, TokenAuthKey, tokenAuth)
|
||||
util.SetToContext(c, TokenAdminKey, tokenAdmin)
|
||||
// set keys in context for session auth
|
||||
util.SetToContext(c, TokenAuthKey, false)
|
||||
util.SetToContext(c, TokenAdminKey, false)
|
||||
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
@@ -197,6 +197,7 @@ func setLoginSession(ctx context.Context, c *gin.Context, user *model.User) erro
|
||||
session := sessions.Default(c)
|
||||
session.Set(UserIDKey, user.ID)
|
||||
session.Set(UserNameKey, user.Username)
|
||||
session.Set(PasswordHashKey, user.Password)
|
||||
|
||||
// 根据系统配置动态设置 Session 过期时间
|
||||
maxAge := config.Config.App.SessionAge
|
||||
|
||||
Reference in New Issue
Block a user