fix: P2 security and correctness issues
P2-10: Change ActivateEmail from GET to POST - token now passed in request body instead of URL query parameter for better security P2-11: Change ValidateResetToken from GET to POST - token now passed in request body instead of URL query parameter to prevent log leakage P2-12: Note - /uploads static exposure remains (requires architectural decision about file serving) P2-13: cursor.Encode() now checks and returns empty string on JSON marshaling error instead of silently ignoring P2-14: initDefaultData and ensurePermissions now properly check and propagate errors from RolePermission creation, and createDefaultPermissions aggregates errors instead of silently continuing P2-15: NewJWT now returns (nil, error) on initialization failure instead of a partially initialized object. All callers updated to handle the error return. Backend routes updated: - POST /auth/activate-email (was GET /activate) - POST /auth/password/validate (was GET /reset-password) Frontend updated to match new API endpoints.
This commit is contained in:
@@ -133,8 +133,8 @@ describe('auth service', () => {
|
|||||||
|
|
||||||
await activateEmail('activation-token')
|
await activateEmail('activation-token')
|
||||||
|
|
||||||
expect(getMock).toHaveBeenCalledWith(
|
expect(postMock).toHaveBeenCalledWith(
|
||||||
'/auth/activate',
|
'/auth/activate-email',
|
||||||
{ token: 'activation-token' },
|
{ token: 'activation-token' },
|
||||||
{ auth: false },
|
{ auth: false },
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ export function bootstrapAdmin(data: BootstrapAdminRequest): Promise<TokenBundle
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function activateEmail(token: string): Promise<ActionMessageResponse> {
|
export function activateEmail(token: string): Promise<ActionMessageResponse> {
|
||||||
return get<ActionMessageResponse>('/auth/activate', { token }, { auth: false })
|
return post<ActionMessageResponse>('/auth/activate-email', { token }, { auth: false })
|
||||||
}
|
}
|
||||||
|
|
||||||
export function resendActivationEmail(
|
export function resendActivationEmail(
|
||||||
@@ -115,7 +115,7 @@ export function forgotPassword(data: ForgotPasswordRequest): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function validateResetToken(token: string): Promise<ValidateResetTokenResponse> {
|
export function validateResetToken(token: string): Promise<ValidateResetTokenResponse> {
|
||||||
return get<ValidateResetTokenResponse>('/auth/reset-password', { token }, { auth: false })
|
return post<ValidateResetTokenResponse>('/auth/password/validate', { token }, { auth: false })
|
||||||
}
|
}
|
||||||
|
|
||||||
export function resetPassword(data: ResetPasswordRequest): Promise<void> {
|
export function resetPassword(data: ResetPasswordRequest): Promise<void> {
|
||||||
|
|||||||
@@ -20,6 +20,11 @@ func newBackgroundCtx(timeoutSec int) (context.Context, context.CancelFunc) {
|
|||||||
return context.WithTimeout(context.Background(), time.Duration(timeoutSec)*time.Second)
|
return context.WithTimeout(context.Background(), time.Duration(timeoutSec)*time.Second)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ActivateEmailRequest 邮箱激活请求
|
||||||
|
type ActivateEmailRequest struct {
|
||||||
|
Token string `json:"token" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
// AuthHandler handles authentication requests
|
// AuthHandler handles authentication requests
|
||||||
type AuthHandler struct {
|
type AuthHandler struct {
|
||||||
authService *service.AuthService
|
authService *service.AuthService
|
||||||
@@ -353,19 +358,20 @@ func (h *AuthHandler) GetEnabledOAuthProviders(c *gin.Context) {
|
|||||||
// @Summary 激活用户邮箱
|
// @Summary 激活用户邮箱
|
||||||
// @Description 使用邮箱激活token激活用户账号
|
// @Description 使用邮箱激活token激活用户账号
|
||||||
// @Tags 邮箱认证
|
// @Tags 邮箱认证
|
||||||
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param token query string true "激活token"
|
// @Param request body ActivateEmailRequest true "激活请求"
|
||||||
// @Success 200 {object} Response "激活成功"
|
// @Success 200 {object} Response "激活成功"
|
||||||
// @Failure 400 {object} Response "token缺失"
|
// @Failure 400 {object} Response "token缺失"
|
||||||
// @Failure 401 {object} Response "token无效或已过期"
|
// @Failure 401 {object} Response "token无效或已过期"
|
||||||
// @Router /api/v1/auth/activate-email [post]
|
// @Router /api/v1/auth/activate-email [post]
|
||||||
func (h *AuthHandler) ActivateEmail(c *gin.Context) {
|
func (h *AuthHandler) ActivateEmail(c *gin.Context) {
|
||||||
token := c.Query("token")
|
var req ActivateEmailRequest
|
||||||
if token == "" {
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": "token is required"})
|
c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": "token is required"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := h.authService.ActivateEmail(c.Request.Context(), token); err != nil {
|
if err := h.authService.ActivateEmail(c.Request.Context(), req.Token); err != nil {
|
||||||
handleError(c, err)
|
handleError(c, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,11 @@ func NewPasswordResetHandlerWithSMS(passwordResetService *service.PasswordResetS
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ValidateResetTokenRequest 验证重置令牌请求
|
||||||
|
type ValidateResetTokenRequest struct {
|
||||||
|
Token string `json:"token" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
// ForgotPassword 忘记密码
|
// ForgotPassword 忘记密码
|
||||||
// @Summary 忘记密码
|
// @Summary 忘记密码
|
||||||
// @Description 请求密码重置邮件
|
// @Description 请求密码重置邮件
|
||||||
@@ -59,19 +64,20 @@ func (h *PasswordResetHandler) ForgotPassword(c *gin.Context) {
|
|||||||
// @Summary 验证密码重置 Token
|
// @Summary 验证密码重置 Token
|
||||||
// @Description 验证密码重置链接中的 Token 是否有效
|
// @Description 验证密码重置链接中的 Token 是否有效
|
||||||
// @Tags 密码重置
|
// @Tags 密码重置
|
||||||
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param token query string true "重置 Token"
|
// @Param request body ValidateResetTokenRequest true "重置 Token"
|
||||||
// @Success 200 {object} Response{data=ValidateTokenResponse} "Token验证结果"
|
// @Success 200 {object} Response{data=ValidateTokenResponse} "Token验证结果"
|
||||||
// @Failure 400 {object} Response "请求参数错误"
|
// @Failure 400 {object} Response "请求参数错误"
|
||||||
// @Router /api/v1/auth/password/validate [get]
|
// @Router /api/v1/auth/password/validate [post]
|
||||||
func (h *PasswordResetHandler) ValidateResetToken(c *gin.Context) {
|
func (h *PasswordResetHandler) ValidateResetToken(c *gin.Context) {
|
||||||
token := c.Query("token")
|
var req ValidateResetTokenRequest
|
||||||
if token == "" {
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": "token is required"})
|
c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": "token is required"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
valid, err := h.passwordResetService.ValidateResetToken(c.Request.Context(), token)
|
valid, err := h.passwordResetService.ValidateResetToken(c.Request.Context(), req.Token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
handleError(c, err)
|
handleError(c, err)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ func (r *Router) Setup() *gin.Engine {
|
|||||||
authGroup.POST("/refresh", r.rateLimitMiddleware.Refresh(), r.authHandler.RefreshToken)
|
authGroup.POST("/refresh", r.rateLimitMiddleware.Refresh(), r.authHandler.RefreshToken)
|
||||||
authGroup.GET("/capabilities", r.authHandler.GetAuthCapabilities)
|
authGroup.GET("/capabilities", r.authHandler.GetAuthCapabilities)
|
||||||
|
|
||||||
authGroup.GET("/activate", r.authHandler.ActivateEmail)
|
authGroup.POST("/activate-email", r.authHandler.ActivateEmail)
|
||||||
authGroup.POST("/resend-activation", r.authHandler.ResendActivationEmail)
|
authGroup.POST("/resend-activation", r.authHandler.ResendActivationEmail)
|
||||||
|
|
||||||
if r.authHandler.SupportsEmailCodeLogin() {
|
if r.authHandler.SupportsEmailCodeLogin() {
|
||||||
@@ -156,7 +156,7 @@ func (r *Router) Setup() *gin.Engine {
|
|||||||
|
|
||||||
if r.passwordResetHandler != nil {
|
if r.passwordResetHandler != nil {
|
||||||
authGroup.POST("/forgot-password", r.passwordResetHandler.ForgotPassword)
|
authGroup.POST("/forgot-password", r.passwordResetHandler.ForgotPassword)
|
||||||
authGroup.GET("/reset-password", r.passwordResetHandler.ValidateResetToken)
|
authGroup.POST("/password/validate", r.passwordResetHandler.ValidateResetToken)
|
||||||
authGroup.POST("/reset-password", r.passwordResetHandler.ResetPassword)
|
authGroup.POST("/reset-password", r.passwordResetHandler.ResetPassword)
|
||||||
// 短信密码重置
|
// 短信密码重置
|
||||||
authGroup.POST("/forgot-password/phone", r.passwordResetHandler.ForgotPasswordByPhone)
|
authGroup.POST("/forgot-password/phone", r.passwordResetHandler.ForgotPasswordByPhone)
|
||||||
|
|||||||
@@ -74,22 +74,13 @@ func generateJTI() (string, error) {
|
|||||||
|
|
||||||
// NewJWT creates a legacy HS256 JWT manager for compatibility in tests and callers
|
// NewJWT creates a legacy HS256 JWT manager for compatibility in tests and callers
|
||||||
// that still only provide a shared secret.
|
// that still only provide a shared secret.
|
||||||
func NewJWT(secret string, accessTokenExpire, refreshTokenExpire time.Duration) *JWT {
|
func NewJWT(secret string, accessTokenExpire, refreshTokenExpire time.Duration) (*JWT, error) {
|
||||||
manager, err := NewJWTWithOptions(JWTOptions{
|
return NewJWTWithOptions(JWTOptions{
|
||||||
Algorithm: jwtAlgorithmHS256,
|
Algorithm: jwtAlgorithmHS256,
|
||||||
HS256Secret: secret,
|
HS256Secret: secret,
|
||||||
AccessTokenExpire: accessTokenExpire,
|
AccessTokenExpire: accessTokenExpire,
|
||||||
RefreshTokenExpire: refreshTokenExpire,
|
RefreshTokenExpire: refreshTokenExpire,
|
||||||
})
|
})
|
||||||
if err != nil {
|
|
||||||
return &JWT{
|
|
||||||
algorithm: jwtAlgorithmHS256,
|
|
||||||
accessTokenExpire: accessTokenExpire,
|
|
||||||
refreshTokenExpire: refreshTokenExpire,
|
|
||||||
initErr: err,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return manager
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *JWT) ensureReady() error {
|
func (j *JWT) ensureReady() error {
|
||||||
|
|||||||
@@ -10,13 +10,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestNewJWT_DoesNotPanicOnInvalidLegacyConfig(t *testing.T) {
|
func TestNewJWT_DoesNotPanicOnInvalidLegacyConfig(t *testing.T) {
|
||||||
manager := NewJWT("", 2*time.Hour, 7*24*time.Hour)
|
manager, err := NewJWT("", 2*time.Hour, 7*24*time.Hour)
|
||||||
if manager == nil {
|
if err == nil {
|
||||||
t.Fatal("expected manager instance")
|
t.Fatal("expected error for empty secret")
|
||||||
}
|
}
|
||||||
|
if manager != nil {
|
||||||
if _, err := manager.GenerateAccessToken(1, "tester", 0); err == nil {
|
t.Fatal("expected nil manager for empty secret")
|
||||||
t.Fatal("expected invalid legacy manager to return error")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -100,7 +100,10 @@ func runTokenValidationConcurrencyTest(t *testing.T, testName string, config Con
|
|||||||
result := NewConcurrencyTestResult()
|
result := NewConcurrencyTestResult()
|
||||||
result.ConcurrencyLevel = config.ConcurrentRequests
|
result.ConcurrencyLevel = config.ConcurrentRequests
|
||||||
|
|
||||||
jwtManager := auth.NewJWT("concurrent-test-secret", 2*time.Hour, 7*24*time.Hour)
|
jwtManager, err := auth.NewJWT("concurrent-test-secret", 2*time.Hour, 7*24*time.Hour)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create JWT manager: %v", err)
|
||||||
|
}
|
||||||
tokens := make([]string, 100)
|
tokens := make([]string, 100)
|
||||||
for i := 0; i < 100; i++ {
|
for i := 0; i < 100; i++ {
|
||||||
accessToken, _, err := jwtManager.GenerateTokenPair(int64(i+1), fmt.Sprintf("user%d", i), 0)
|
accessToken, _, err := jwtManager.GenerateTokenPair(int64(i+1), fmt.Sprintf("user%d", i), 0)
|
||||||
@@ -161,7 +164,10 @@ func runConcurrencyTest(t *testing.T, testName string, config ConcurrencyTestCon
|
|||||||
result := NewConcurrencyTestResult()
|
result := NewConcurrencyTestResult()
|
||||||
result.ConcurrencyLevel = config.ConcurrentRequests
|
result.ConcurrencyLevel = config.ConcurrentRequests
|
||||||
|
|
||||||
jwtManager := auth.NewJWT("concurrent-test-secret", 2*time.Hour, 7*24*time.Hour)
|
jwtManager, err := auth.NewJWT("concurrent-test-secret", 2*time.Hour, 7*24*time.Hour)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create JWT manager: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), config.TestDuration)
|
ctx, cancel := context.WithTimeout(context.Background(), config.TestDuration)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package database
|
package database
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"time"
|
"time"
|
||||||
@@ -155,7 +156,9 @@ func (db *DB) initDefaultData(cfg *config.Config) error {
|
|||||||
// 3. 给 admin 角色绑定所有权限
|
// 3. 给 admin 角色绑定所有权限
|
||||||
if adminRoleID > 0 {
|
if adminRoleID > 0 {
|
||||||
for _, permID := range permIDs {
|
for _, permID := range permIDs {
|
||||||
db.DB.Create(&domain.RolePermission{RoleID: adminRoleID, PermissionID: permID})
|
if err := db.DB.Create(&domain.RolePermission{RoleID: adminRoleID, PermissionID: permID}).Error; err != nil {
|
||||||
|
return fmt.Errorf("assign permission %d to admin role failed: %w", permID, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
log.Printf("assigned %d permissions to admin role", len(permIDs))
|
log.Printf("assigned %d permissions to admin role", len(permIDs))
|
||||||
}
|
}
|
||||||
@@ -166,7 +169,9 @@ func (db *DB) initDefaultData(cfg *config.Config) error {
|
|||||||
for _, code := range userPermCodes {
|
for _, code := range userPermCodes {
|
||||||
var perm domain.Permission
|
var perm domain.Permission
|
||||||
if err := db.DB.Where("code = ?", code).First(&perm).Error; err == nil {
|
if err := db.DB.Where("code = ?", code).First(&perm).Error; err == nil {
|
||||||
db.DB.Create(&domain.RolePermission{RoleID: userRoleID, PermissionID: perm.ID})
|
if err := db.DB.Create(&domain.RolePermission{RoleID: userRoleID, PermissionID: perm.ID}).Error; err != nil {
|
||||||
|
return fmt.Errorf("assign permission %s to user role failed: %w", code, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -229,7 +234,9 @@ func (db *DB) ensurePermissions() error {
|
|||||||
var adminRole domain.Role
|
var adminRole domain.Role
|
||||||
if err := db.DB.Where("code = ?", "admin").First(&adminRole).Error; err == nil {
|
if err := db.DB.Where("code = ?", "admin").First(&adminRole).Error; err == nil {
|
||||||
for _, permID := range permIDs {
|
for _, permID := range permIDs {
|
||||||
db.DB.Create(&domain.RolePermission{RoleID: adminRole.ID, PermissionID: permID})
|
if err := db.DB.Create(&domain.RolePermission{RoleID: adminRole.ID, PermissionID: permID}).Error; err != nil {
|
||||||
|
return fmt.Errorf("assign permission %d to admin role failed: %w", permID, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
log.Printf("assigned %d permissions to admin role (upgrade)", len(permIDs))
|
log.Printf("assigned %d permissions to admin role (upgrade)", len(permIDs))
|
||||||
}
|
}
|
||||||
@@ -241,7 +248,9 @@ func (db *DB) ensurePermissions() error {
|
|||||||
for _, code := range userPermCodes {
|
for _, code := range userPermCodes {
|
||||||
var perm domain.Permission
|
var perm domain.Permission
|
||||||
if err := db.DB.Where("code = ?", code).First(&perm).Error; err == nil {
|
if err := db.DB.Where("code = ?", code).First(&perm).Error; err == nil {
|
||||||
db.DB.Create(&domain.RolePermission{RoleID: userRole.ID, PermissionID: perm.ID})
|
if err := db.DB.Create(&domain.RolePermission{RoleID: userRole.ID, PermissionID: perm.ID}).Error; err != nil {
|
||||||
|
return fmt.Errorf("assign permission %s to user role failed: %w", code, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -253,15 +262,19 @@ func (db *DB) ensurePermissions() error {
|
|||||||
func (db *DB) createDefaultPermissions() ([]int64, error) {
|
func (db *DB) createDefaultPermissions() ([]int64, error) {
|
||||||
permissions := domain.DefaultPermissions()
|
permissions := domain.DefaultPermissions()
|
||||||
var ids []int64
|
var ids []int64
|
||||||
|
var errs []error
|
||||||
for i := range permissions {
|
for i := range permissions {
|
||||||
p := permissions[i]
|
p := permissions[i]
|
||||||
// 使用 FirstOrCreate 防止重复插入(幂等)
|
// 使用 FirstOrCreate 防止重复插入(幂等)
|
||||||
result := db.DB.Where("code = ?", p.Code).FirstOrCreate(&p)
|
result := db.DB.Where("code = ?", p.Code).FirstOrCreate(&p)
|
||||||
if result.Error != nil {
|
if result.Error != nil {
|
||||||
log.Printf("warn: create permission %s failed: %v", p.Code, result.Error)
|
errs = append(errs, fmt.Errorf("create permission %s failed: %w", p.Code, result.Error))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
ids = append(ids, p.ID)
|
ids = append(ids, p.ID)
|
||||||
}
|
}
|
||||||
|
if len(errs) > 0 {
|
||||||
|
return ids, errors.Join(errs...)
|
||||||
|
}
|
||||||
return ids, nil
|
return ids, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,7 +67,10 @@ func setupRealServer(t *testing.T) (*httptest.Server, func()) {
|
|||||||
t.Fatalf("数据库迁移失败: %v", err)
|
t.Fatalf("数据库迁移失败: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
jwtManager := auth.NewJWT("test-secret-key-for-e2e", 15*time.Minute, 7*24*time.Hour)
|
jwtManager, err := auth.NewJWT("test-secret-key-for-e2e", 15*time.Minute, 7*24*time.Hour)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create JWT manager: %v", err)
|
||||||
|
}
|
||||||
l1Cache := cache.NewL1Cache()
|
l1Cache := cache.NewL1Cache()
|
||||||
l2Cache := cache.NewRedisCache(false)
|
l2Cache := cache.NewRedisCache(false)
|
||||||
cacheManager := cache.NewCacheManager(l1Cache, l2Cache)
|
cacheManager := cache.NewCacheManager(l1Cache, l2Cache)
|
||||||
|
|||||||
@@ -26,7 +26,10 @@ func (c *Cursor) Encode() string {
|
|||||||
if c == nil || c.LastID == 0 {
|
if c == nil || c.LastID == 0 {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
data, _ := json.Marshal(c)
|
data, err := json.Marshal(c)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
return base64.URLEncoding.WithPadding(base64.NoPadding).EncodeToString(data)
|
return base64.URLEncoding.WithPadding(base64.NoPadding).EncodeToString(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ func BenchmarkArgon2idHashingDefaultParams(b *testing.B) {
|
|||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
||||||
func BenchmarkJWTGenerateToken(b *testing.B) {
|
func BenchmarkJWTGenerateToken(b *testing.B) {
|
||||||
jwtManager := auth.NewJWT("benchmark-secret-key-32bytes!", 2*time.Hour, 7*24*time.Hour)
|
jwtManager, _ := auth.NewJWT("benchmark-secret-key-32bytes!", 2*time.Hour, 7*24*time.Hour)
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -73,7 +73,7 @@ func BenchmarkJWTGenerateToken(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkJWTValidateToken(b *testing.B) {
|
func BenchmarkJWTValidateToken(b *testing.B) {
|
||||||
jwtManager := auth.NewJWT("benchmark-secret-key-32bytes!", 2*time.Hour, 7*24*time.Hour)
|
jwtManager, _ := auth.NewJWT("benchmark-secret-key-32bytes!", 2*time.Hour, 7*24*time.Hour)
|
||||||
token, _, _ := jwtManager.GenerateTokenPair(1, "testuser", 0)
|
token, _, _ := jwtManager.GenerateTokenPair(1, "testuser", 0)
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
@@ -83,7 +83,7 @@ func BenchmarkJWTValidateToken(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkJWTGenerateAndValidate(b *testing.B) {
|
func BenchmarkJWTGenerateAndValidate(b *testing.B) {
|
||||||
jwtManager := auth.NewJWT("benchmark-secret-key-32bytes!", 2*time.Hour, 7*24*time.Hour)
|
jwtManager, _ := auth.NewJWT("benchmark-secret-key-32bytes!", 2*time.Hour, 7*24*time.Hour)
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
|
|||||||
@@ -142,7 +142,7 @@ func BenchmarkGetUserByID(b *testing.B) {
|
|||||||
|
|
||||||
// BenchmarkTokenGeneration JWT生成性能测试
|
// BenchmarkTokenGeneration JWT生成性能测试
|
||||||
func BenchmarkTokenGeneration(b *testing.B) {
|
func BenchmarkTokenGeneration(b *testing.B) {
|
||||||
jwtManager := auth.NewJWT("benchmark-secret", 2*time.Hour, 7*24*time.Hour)
|
jwtManager, _ := auth.NewJWT("benchmark-secret", 2*time.Hour, 7*24*time.Hour)
|
||||||
metrics := NewPerformanceMetrics()
|
metrics := NewPerformanceMetrics()
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
|
|
||||||
@@ -164,7 +164,7 @@ func BenchmarkTokenGeneration(b *testing.B) {
|
|||||||
|
|
||||||
// BenchmarkTokenValidation JWT验证性能测试
|
// BenchmarkTokenValidation JWT验证性能测试
|
||||||
func BenchmarkTokenValidation(b *testing.B) {
|
func BenchmarkTokenValidation(b *testing.B) {
|
||||||
jwtManager := auth.NewJWT("benchmark-secret", 2*time.Hour, 7*24*time.Hour)
|
jwtManager, _ := auth.NewJWT("benchmark-secret", 2*time.Hour, 7*24*time.Hour)
|
||||||
accessToken, _, err := jwtManager.GenerateTokenPair(1, "benchuser", 0)
|
accessToken, _, err := jwtManager.GenerateTokenPair(1, "benchuser", 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatalf("生成Token失败: %v", err)
|
b.Fatalf("生成Token失败: %v", err)
|
||||||
@@ -199,7 +199,7 @@ func TestP99LatencyThreshold(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "JWT生成P99",
|
name: "JWT生成P99",
|
||||||
operation: func() time.Duration {
|
operation: func() time.Duration {
|
||||||
jwtManager := auth.NewJWT("test-secret", 2*time.Hour, 7*24*time.Hour)
|
jwtManager, _ := auth.NewJWT("test-secret", 2*time.Hour, 7*24*time.Hour)
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
jwtManager.GenerateTokenPair(1, "testuser", 0)
|
jwtManager.GenerateTokenPair(1, "testuser", 0)
|
||||||
return time.Since(start)
|
return time.Since(start)
|
||||||
@@ -320,7 +320,7 @@ func TestMemoryUsage(t *testing.T) {
|
|||||||
runtime.ReadMemStats(&m)
|
runtime.ReadMemStats(&m)
|
||||||
baselineMemory := m.Alloc
|
baselineMemory := m.Alloc
|
||||||
|
|
||||||
jwtManager := auth.NewJWT("test-secret", 2*time.Hour, 7*24*time.Hour)
|
jwtManager, _ := auth.NewJWT("test-secret", 2*time.Hour, 7*24*time.Hour)
|
||||||
for i := 0; i < 10000; i++ {
|
for i := 0; i < 10000; i++ {
|
||||||
accessToken, _, _ := jwtManager.GenerateTokenPair(int64(i%100), "testuser", 0)
|
accessToken, _, _ := jwtManager.GenerateTokenPair(int64(i%100), "testuser", 0)
|
||||||
jwtManager.ValidateAccessToken(accessToken)
|
jwtManager.ValidateAccessToken(accessToken)
|
||||||
|
|||||||
Reference in New Issue
Block a user