feat: 初始提交云印享付费打印服务后端

This commit is contained in:
WuYuuuub
2026-03-29 11:55:53 +08:00
parent 5047380dd0
commit 89a7c87bf6
35 changed files with 12522 additions and 0 deletions

View File

@@ -0,0 +1,125 @@
package interceptor
import (
"context"
"strings"
"github.com/cloudprint/cloudprint-backend/pkg/jwt"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
)
const (
AuthorizationHeader = "authorization"
BearerPrefix = "Bearer "
UserIDKey = "user_id"
UserTypeKey = "user_type"
)
type AuthInterceptor struct {
jwtManager *jwt.JWTManager
}
func NewAuthInterceptor(jwtManager *jwt.JWTManager) *AuthInterceptor {
return &AuthInterceptor{jwtManager: jwtManager}
}
// UnaryServerInterceptor returns a unary server interceptor that handles authentication
func (i *AuthInterceptor) UnaryServerInterceptor() grpc.UnaryServerInterceptor {
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
// Skip auth for certain methods
if isPublicMethod(info.FullMethod) {
return handler(ctx, req)
}
claims, err := i.extractAndValidateToken(ctx)
if err != nil {
return nil, status.Errorf(codes.Unauthenticated, "invalid token: %v", err)
}
// Add user info to context
ctx = context.WithValue(ctx, UserIDKey, claims.UserID)
ctx = context.WithValue(ctx, UserTypeKey, claims.UserType)
return handler(ctx, req)
}
}
// StreamServerInterceptor returns a streaming server interceptor that handles authentication
func (i *AuthInterceptor) StreamServerInterceptor() grpc.StreamServerInterceptor {
return func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
// Skip auth for certain methods
if isPublicMethod(info.FullMethod) {
return handler(srv, ss)
}
claims, err := i.extractAndValidateToken(ss.Context())
if err != nil {
return status.Errorf(codes.Unauthenticated, "invalid token: %v", err)
}
// Add user info to context
ctx := context.WithValue(ss.Context(), UserIDKey, claims.UserID)
ctx = context.WithValue(ctx, UserTypeKey, claims.UserType)
wrapped := &serverStreamWrapper{
ServerStream: ss,
ctx: ctx,
}
return handler(srv, wrapped)
}
}
func (i *AuthInterceptor) extractAndValidateToken(ctx context.Context) (*jwt.Claims, error) {
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
return nil, status.Error(codes.Unauthenticated, "missing metadata")
}
authHeader := md.Get(AuthorizationHeader)
if len(authHeader) == 0 {
return nil, status.Error(codes.Unauthenticated, "missing authorization header")
}
tokenString := strings.TrimPrefix(authHeader[0], BearerPrefix)
if tokenString == authHeader[0] {
return nil, status.Error(codes.Unauthenticated, "invalid authorization format")
}
return i.jwtManager.ValidateToken(tokenString)
}
func isPublicMethod(method string) bool {
publicMethods := map[string]bool{
"/cloudprint.AuthService/Login": true,
"/cloudprint.AuthService/RefreshToken": true,
"/cloudprint.AdminService/Login": true,
"/cloudprint.PayService/HandlePayCallback": true,
}
return publicMethods[method]
}
// serverStreamWrapper wraps grpc.ServerStream to override Context method
type serverStreamWrapper struct {
grpc.ServerStream
ctx context.Context
}
func (w *serverStreamWrapper) Context() context.Context {
return w.ctx
}
// GetUserIDFromContext extracts user ID from context
func GetUserIDFromContext(ctx context.Context) (int32, bool) {
userID, ok := ctx.Value(UserIDKey).(int32)
return userID, ok
}
// GetUserTypeFromContext extracts user type from context
func GetUserTypeFromContext(ctx context.Context) (string, bool) {
userType, ok := ctx.Value(UserTypeKey).(string)
return userType, ok
}