## 主要变更 ### 🏗️ 架构重构 - 采用简洁的四层架构:API → Service → Repository → Model - 遵循 Go 语言最佳实践和命名规范 - 实现依赖注入和接口导向设计 - 统一错误处理和响应格式 ### 📁 目录结构优化 - 删除重复模块 (application/, domain/, infrastructure/ 等) - 规范化命名 (使用 Go 风格的 snake_case) - 清理无关文件 (package.json, node_modules/ 等) - 新增规范化的测试目录结构 ### 📚 文档系统 - 为每个模块创建详细的 CLAUDE.md 指导文件 - 包含开发规范、最佳实践和使用示例 - 支持模块化开发,缩短上下文长度 ### 🔧 开发规范 - 统一接口命名规范 (UserServicer, PhotoRepositoryr) - 标准化错误处理机制 - 完善的测试策略 (单元测试、集成测试、性能测试) - 规范化的配置管理 ### 🗂️ 新增文件 - cmd/server/ - 服务启动入口和配置 - internal/model/ - 数据模型层 (entity, dto, request) - pkg/ - 共享工具包 (logger, response, validator) - tests/ - 完整测试结构 - docs/ - API 文档和架构设计 - .gitignore - Git 忽略文件配置 ### 🗑️ 清理内容 - 删除 Node.js 相关文件 (package.json, node_modules/) - 移除重复的架构目录 - 清理临时文件和构建产物 - 删除重复的文档文件 ## 影响 - 提高代码可维护性和可扩展性 - 统一开发规范,提升团队协作效率 - 优化项目结构,符合 Go 语言生态标准 - 完善文档体系,降低上手难度
132 lines
3.6 KiB
Go
132 lines
3.6 KiB
Go
package auth
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
"github.com/golang-jwt/jwt/v5"
|
|
"photography-backend/internal/config"
|
|
)
|
|
|
|
// JWTService JWT服务
|
|
type JWTService struct {
|
|
secretKey []byte
|
|
accessTokenDuration time.Duration
|
|
refreshTokenDuration time.Duration
|
|
}
|
|
|
|
// JWTClaims JWT声明
|
|
type JWTClaims struct {
|
|
UserID uint `json:"user_id"`
|
|
Username string `json:"username"`
|
|
Role string `json:"role"`
|
|
jwt.RegisteredClaims
|
|
}
|
|
|
|
// TokenPair 令牌对
|
|
type TokenPair struct {
|
|
AccessToken string `json:"access_token"`
|
|
RefreshToken string `json:"refresh_token"`
|
|
TokenType string `json:"token_type"`
|
|
ExpiresIn int64 `json:"expires_in"`
|
|
}
|
|
|
|
// NewJWTService 创建JWT服务
|
|
func NewJWTService(cfg *config.JWTConfig) *JWTService {
|
|
accessDuration, _ := time.ParseDuration(cfg.ExpiresIn)
|
|
refreshDuration, _ := time.ParseDuration(cfg.RefreshExpiresIn)
|
|
|
|
return &JWTService{
|
|
secretKey: []byte(cfg.Secret),
|
|
accessTokenDuration: accessDuration,
|
|
refreshTokenDuration: refreshDuration,
|
|
}
|
|
}
|
|
|
|
// GenerateTokenPair 生成令牌对
|
|
func (s *JWTService) GenerateTokenPair(userID uint, username, role string) (*TokenPair, error) {
|
|
// 生成访问令牌
|
|
accessToken, err := s.generateToken(userID, username, role, s.accessTokenDuration)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to generate access token: %w", err)
|
|
}
|
|
|
|
// 生成刷新令牌
|
|
refreshToken, err := s.generateToken(userID, username, role, s.refreshTokenDuration)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to generate refresh token: %w", err)
|
|
}
|
|
|
|
return &TokenPair{
|
|
AccessToken: accessToken,
|
|
RefreshToken: refreshToken,
|
|
TokenType: "Bearer",
|
|
ExpiresIn: int64(s.accessTokenDuration.Seconds()),
|
|
}, nil
|
|
}
|
|
|
|
// generateToken 生成令牌
|
|
func (s *JWTService) generateToken(userID uint, username, role string, duration time.Duration) (string, error) {
|
|
now := time.Now()
|
|
claims := &JWTClaims{
|
|
UserID: userID,
|
|
Username: username,
|
|
Role: role,
|
|
RegisteredClaims: jwt.RegisteredClaims{
|
|
ExpiresAt: jwt.NewNumericDate(now.Add(duration)),
|
|
IssuedAt: jwt.NewNumericDate(now),
|
|
NotBefore: jwt.NewNumericDate(now),
|
|
Issuer: "photography-backend",
|
|
},
|
|
}
|
|
|
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
|
return token.SignedString(s.secretKey)
|
|
}
|
|
|
|
// ValidateToken 验证令牌
|
|
func (s *JWTService) ValidateToken(tokenString string) (*JWTClaims, error) {
|
|
token, err := jwt.ParseWithClaims(tokenString, &JWTClaims{}, func(token *jwt.Token) (interface{}, error) {
|
|
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
|
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
|
|
}
|
|
return s.secretKey, nil
|
|
})
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to parse token: %w", err)
|
|
}
|
|
|
|
if claims, ok := token.Claims.(*JWTClaims); ok && token.Valid {
|
|
return claims, nil
|
|
}
|
|
|
|
return nil, fmt.Errorf("invalid token")
|
|
}
|
|
|
|
// RefreshToken 刷新令牌
|
|
func (s *JWTService) RefreshToken(refreshToken string) (*TokenPair, error) {
|
|
claims, err := s.ValidateToken(refreshToken)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid refresh token: %w", err)
|
|
}
|
|
|
|
// 生成新的令牌对
|
|
return s.GenerateTokenPair(claims.UserID, claims.Username, claims.Role)
|
|
}
|
|
|
|
// GetClaimsFromToken 从令牌中获取声明
|
|
func (s *JWTService) GetClaimsFromToken(tokenString string) (*JWTClaims, error) {
|
|
token, err := jwt.ParseWithClaims(tokenString, &JWTClaims{}, func(token *jwt.Token) (interface{}, error) {
|
|
return s.secretKey, nil
|
|
})
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if claims, ok := token.Claims.(*JWTClaims); ok {
|
|
return claims, nil
|
|
}
|
|
|
|
return nil, fmt.Errorf("invalid claims")
|
|
} |