Files
backend/internal/service/jwt_service.go
lafay eb931bf1c6
All checks were successful
Build Backend / build (push) Successful in 2m19s
Build Backend / build-docker (push) Successful in 46s
feat(auth): implement session-based token management with revocation support
Add Session model, SessionService, and SessionRepository to track user login
sessions and enable token revocation on auth-critical events.

- Introduce explicit TokenType (access/refresh) in JWT claims to prevent
  refresh token misuse via access token endpoints
- Add SessionID field to JWT claims, enabling stateless JWT validation
  against revoked sessions
- Replace legacy Auth middleware with RequireAuth/OptionalAuth pipeline
  that validates token type, account status, and session validity
- Implement session revocation on password change, reset, user ban/inactive,
  and explicit logout
- Add Principal cache with active invalidation for banned/role-changed users
- Fix IDOR vulnerability: GetMessagesByCursor now validates currentUserID
  is conversation participant via GetParticipantStrict
- Add group member visibility checks: announcements, group info, member list
  now require group membership
- Simplify Casbin policy: remove g grouping, use r.sub == p.sub matcher
  with globMatch; user_roles table is single source of truth for roles
- Add migration logic to clean legacy casbin g rules and migrate old p rules
  from path-style to admin/<domain> resource naming
2026-07-05 18:28:08 +08:00

89 lines
3.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package service
import (
"time"
"with_you/internal/pkg/jwt"
)
// JWTService JWT服务接口
//
// 拆分为按令牌类型的解析 API避免历史单一 ParseToken 让 refresh token 误当 access token 使用。
// 旧的 GenerateAccessToken/GenerateRefreshToken 签名保留兼容;带 Session 的版本由 SessionService
// 配合使用,把 sid 写入 claims 支持撤销。
type JWTService interface {
GenerateAccessToken(userID, username string) (string, error)
GenerateRefreshToken(userID, username string) (string, error)
// 带会话 ID 的签发sid 写入 claims供 RequireAuth 校验会话有效性。
GenerateAccessTokenWithSession(userID, username, sessionID string) (string, error)
GenerateRefreshTokenWithSession(userID, username, sessionID string) (string, error)
// 按令牌类型严格解析。
ParseAccessToken(tokenString string) (*jwt.Claims, error)
ParseRefreshToken(tokenString string) (*jwt.Claims, error)
// Deprecated: 仅做签名+过期校验,不区分令牌类型。新代码应使用 ParseAccessToken/ParseRefreshToken。
ParseToken(tokenString string) (*jwt.Claims, error)
// Deprecated: 新代码直接用 ParseAccessToken。保留仅为历史调用点平滑迁移。
ValidateToken(tokenString string) error
}
// jwtServiceImpl JWT服务实现
type jwtServiceImpl struct {
jwt *jwt.JWT
}
// NewJWTService 创建JWT服务
func NewJWTService(secret string, accessExpire, refreshExpire int64) JWTService {
return &jwtServiceImpl{
jwt: jwt.New(secret, time.Duration(accessExpire)*time.Second, time.Duration(refreshExpire)*time.Second),
}
}
// GenerateAccessToken 生成访问令牌(不带 sid仅向后兼容
func (s *jwtServiceImpl) GenerateAccessToken(userID, username string) (string, error) {
return s.jwt.GenerateAccessToken(userID, username, "")
}
// GenerateRefreshToken 生成刷新令牌(不带 sid仅向后兼容
func (s *jwtServiceImpl) GenerateRefreshToken(userID, username string) (string, error) {
return s.jwt.GenerateRefreshToken(userID, username, "")
}
// GenerateAccessTokenWithSession 生成带会话 ID 的访问令牌。
func (s *jwtServiceImpl) GenerateAccessTokenWithSession(userID, username, sessionID string) (string, error) {
return s.jwt.GenerateAccessToken(userID, username, sessionID)
}
// GenerateRefreshTokenWithSession 生成带会话 ID 的刷新令牌。
func (s *jwtServiceImpl) GenerateRefreshTokenWithSession(userID, username, sessionID string) (string, error) {
return s.jwt.GenerateRefreshToken(userID, username, sessionID)
}
// ParseAccessToken 解析访问令牌(仅接受 typ=access
func (s *jwtServiceImpl) ParseAccessToken(tokenString string) (*jwt.Claims, error) {
return s.jwt.ParseAccessToken(tokenString)
}
// ParseRefreshToken 解析刷新令牌(仅接受 typ=refresh
func (s *jwtServiceImpl) ParseRefreshToken(tokenString string) (*jwt.Claims, error) {
return s.jwt.ParseRefreshToken(tokenString)
}
// ParseToken 解析令牌(兼容签名)。
//
// Deprecated: 新代码使用 ParseAccessToken / ParseRefreshToken。
// 这里内部仍走 ParseAccessToken 以拒绝 refresh token避免历史调用点继续把 refresh 当 access 使用。
func (s *jwtServiceImpl) ParseToken(tokenString string) (*jwt.Claims, error) {
return s.jwt.ParseToken(tokenString)
}
// ValidateToken 验证令牌。
//
// Deprecated: 新代码直接使用 ParseAccessToken。
func (s *jwtServiceImpl) ValidateToken(tokenString string) error {
return s.jwt.ValidateToken(tokenString)
}