基于Spring Cloud Gateway的微服务网关设计:限流、熔断、认证一体化架构

小雨
小雨 2026-02-06T03:05:01+08:00
0 0 1

引言

在现代微服务架构中,API网关作为系统入口层扮演着至关重要的角色。它不仅负责请求路由,还承担着流量控制、服务熔断、安全认证等核心功能。Spring Cloud Gateway作为Spring Cloud生态中的新一代API网关,凭借其基于Netty的异步非阻塞特性,为构建高性能、高可用的微服务网关提供了强大的技术支持。

本文将深入探讨如何基于Spring Cloud Gateway构建一个集限流、熔断、认证于一体的微服务网关架构,通过详细的代码示例和最佳实践,帮助开发者打造稳定可靠的微服务入口层。

Spring Cloud Gateway核心概念

什么是Spring Cloud Gateway

Spring Cloud Gateway是Spring Cloud生态中的API网关组件,它基于Spring Boot 2.x和Spring WebFlux构建,采用响应式编程模型。与传统的Zuul相比,Gateway具有更高的性能和更好的可扩展性。

Gateway的核心特性包括:

  • 基于Netty的异步非阻塞IO
  • 支持路由匹配、过滤器链处理
  • 内置负载均衡、熔断机制
  • 灵活的路由配置和动态刷新

核心组件架构

# Gateway核心组件关系图
┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│   Route     │───▶│   Predicate │───▶│   Filter    │
└─────────────┘    └─────────────┘    └─────────────┘
       ▲                    ▲              ▲
       │                    │              │
┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│  Gateway    │◀───│   Router    │◀───│   Gateway   │
│   Server    │    │   Handler   │    │   Filter    │
└─────────────┘    └─────────────┘    └─────────────┘

基础环境搭建

项目依赖配置

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.0</version>
        <relativePath/>
    </parent>
    
    <groupId>com.example</groupId>
    <artifactId>gateway-service</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>gateway-service</name>
    
    <properties>
        <java.version>11</java.version>
        <spring-cloud.version>2021.0.3</spring-cloud.version>
    </properties>
    
    <dependencies>
        <!-- Spring Cloud Gateway -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        
        <!-- Spring Cloud LoadBalancer -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
        </dependency>
        
        <!-- Spring Cloud Security -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        
        <!-- JWT处理 -->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>
        
        <!-- Redis用于限流 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
        </dependency>
        
        <!-- Actuator监控 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
    </dependencies>
    
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

配置文件设置

# application.yml
server:
  port: 8080

spring:
  application:
    name: gateway-service
  cloud:
    gateway:
      routes:
        # 用户服务路由
        - id: user-service
          uri: lb://user-service
          predicates:
            - Path=/api/user/**
          filters:
            - name: RateLimiter
              args:
                key-resolver: "#{@userKeyResolver}"
                redis-rate-limiter.replenishRate: 10
                redis-rate-limiter.burst: 20
        # 订单服务路由
        - id: order-service
          uri: lb://order-service
          predicates:
            - Path=/api/order/**
          filters:
            - name: CircuitBreaker
              args:
                name: order-service-circuit-breaker
        # 商品服务路由
        - id: product-service
          uri: lb://product-service
          predicates:
            - Path=/api/product/**
          filters:
            - name: RateLimiter
              args:
                key-resolver: "#{@productKeyResolver}"
                redis-rate-limiter.replenishRate: 5
                redis-rate-limiter.burst: 10
      # 全局过滤器
      global-filters:
        - name: AuthFilter
          args:
            skip-urls: /api/auth/login,/api/auth/register
        - name: CircuitBreaker
          args:
            name: global-circuit-breaker
      # 默认超时时间
      httpclient:
        connect-timeout: 1000
        response-timeout: 5000
  redis:
    host: localhost
    port: 6379
    timeout: 2000ms

# 安全配置
jwt:
  secret: mySecretKey1234567890
  expiration: 86400000

management:
  endpoints:
    web:
      exposure:
        include: health,info,route,metrics
  endpoint:
    health:
      show-details: always

路由配置与管理

基于YAML的路由配置

spring:
  cloud:
    gateway:
      routes:
        # 用户服务路由 - 包含认证和限流
        - id: user-service
          uri: lb://user-service
          predicates:
            - Path=/api/user/**
            - Method=GET,POST,PUT,DELETE
          filters:
            - name: AuthFilter
            - name: RateLimiter
              args:
                key-resolver: "#{@userKeyResolver}"
                redis-rate-limiter.replenishRate: 10
                redis-rate-limiter.burst: 20
            - name: CircuitBreaker
              args:
                name: user-service-circuit-breaker
                fallbackUri: forward:/fallback/user
        # 订单服务路由 - 包含熔断和认证
        - id: order-service
          uri: lb://order-service
          predicates:
            - Path=/api/order/**
          filters:
            - name: AuthFilter
            - name: CircuitBreaker
              args:
                name: order-service-circuit-breaker
                fallbackUri: forward:/fallback/order
        # 商品服务路由 - 包含限流和熔断
        - id: product-service
          uri: lb://product-service
          predicates:
            - Path=/api/product/**
          filters:
            - name: RateLimiter
              args:
                key-resolver: "#{@productKeyResolver}"
                redis-rate-limiter.replenishRate: 5
                redis-rate-limiter.burst: 10
            - name: CircuitBreaker
              args:
                name: product-service-circuit-breaker

动态路由配置

@Component
public class DynamicRouteService {
    
    @Autowired
    private RouteDefinitionLocator routeDefinitionLocator;
    
    @Autowired
    private RouteDefinitionWriter routeDefinitionWriter;
    
    @Autowired
    private RouteRefreshable routeRefreshable;
    
    /**
     * 添加动态路由
     */
    public void addRoute(RouteDefinition routeDefinition) {
        try {
            routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe();
            routeRefreshable.refresh();
        } catch (Exception e) {
            log.error("添加路由失败", e);
        }
    }
    
    /**
     * 删除动态路由
     */
    public void deleteRoute(String id) {
        try {
            routeDefinitionWriter.delete(Mono.just(id)).subscribe();
            routeRefreshable.refresh();
        } catch (Exception e) {
            log.error("删除路由失败", e);
        }
    }
}

限流机制实现

Redis限流器核心实现

@Component
public class RateLimiterConfig {
    
    @Bean
    public KeyResolver userKeyResolver() {
        return exchange -> {
            ServerHttpRequest request = exchange.getRequest();
            String userId = getUserIdFromRequest(request);
            if (userId == null) {
                userId = "anonymous";
            }
            return Mono.just(userId);
        };
    }
    
    @Bean
    public KeyResolver productKeyResolver() {
        return exchange -> {
            ServerHttpRequest request = exchange.getRequest();
            String productId = getProductFromRequest(request);
            if (productId == null) {
                productId = "default";
            }
            return Mono.just(productId);
        };
    }
    
    private String getUserIdFromRequest(ServerHttpRequest request) {
        // 从请求头或参数中提取用户ID
        String userId = request.getHeaders().getFirst("X-User-ID");
        if (userId == null) {
            userId = request.getQueryParams().getFirst("userId");
        }
        return userId;
    }
    
    private String getProductFromRequest(ServerHttpRequest request) {
        // 从路径参数中提取商品ID
        String path = request.getPath().toString();
        if (path.contains("/api/product/")) {
            String[] parts = path.split("/");
            if (parts.length >= 5) {
                return parts[4];
            }
        }
        return null;
    }
}

自定义限流过滤器

@Component
public class RateLimitFilter implements GatewayFilter, Ordered {
    
    private static final Logger log = LoggerFactory.getLogger(RateLimitFilter.class);
    
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    @Value("${rate-limiter.redis.prefix:rate_limit}")
    private String prefix;
    
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        
        // 获取限流配置
        RateLimitConfig config = getRateLimitConfig(request);
        if (config == null) {
            return chain.filter(exchange);
        }
        
        String key = generateKey(request, config);
        return checkAndApplyRateLimit(key, config, exchange, chain);
    }
    
    private Mono<Void> checkAndApplyRateLimit(String key, RateLimitConfig config,
                                            ServerWebExchange exchange, GatewayFilterChain chain) {
        String redisKey = prefix + ":" + key;
        
        // 使用Redis Lua脚本实现原子性限流
        String script = "local key = KEYS[1] " +
                       "local limit = tonumber(ARGV[1]) " +
                       "local replenishRate = tonumber(ARGV[2]) " +
                       "local burst = tonumber(ARGV[3]) " +
                       "local now = tonumber(ARGV[4]) " +
                       "local lastRefillTime = redis.call('HGET', key, 'lastRefillTime') " +
                       "local tokens = redis.call('HGET', key, 'tokens') " +
                       "if not lastRefillTime then " +
                       "  redis.call('HSET', key, 'lastRefillTime', now) " +
                       "  redis.call('HSET', key, 'tokens', replenishRate) " +
                       "  tokens = replenishRate " +
                       "else " +
                       "  local timePassed = now - lastRefillTime " +
                       "  local newTokens = math.min(replenishRate, tokens + (timePassed * replenishRate)) " +
                       "  if newTokens > burst then " +
                       "    newTokens = burst " +
                       "  end " +
                       "  redis.call('HSET', key, 'tokens', newTokens) " +
                       "  redis.call('HSET', key, 'lastRefillTime', now) " +
                       "  tokens = newTokens " +
                       "end " +
                       "if tokens >= 1 then " +
                       "  redis.call('HSET', key, 'tokens', tokens - 1) " +
                       "  return 1 " +
                       "else " +
                       "  return 0 " +
                       "end";
        
        try {
            List<Object> result = (List<Object>) redisTemplate.execute(
                new DefaultRedisScript<>(script, Long.class),
                Arrays.asList(redisKey),
                String.valueOf(config.getLimit()),
                String.valueOf(config.getReplenishRate()),
                String.valueOf(config.getBurst()),
                String.valueOf(System.currentTimeMillis())
            );
            
            if (result != null && result.get(0).equals(1L)) {
                return chain.filter(exchange);
            } else {
                // 限流拒绝
                response.setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
                response.getHeaders().add("Retry-After", "1");
                return response.setComplete();
            }
        } catch (Exception e) {
            log.error("限流检查失败", e);
            return chain.filter(exchange);
        }
    }
    
    private RateLimitConfig getRateLimitConfig(ServerHttpRequest request) {
        // 根据请求路径获取对应的限流配置
        String path = request.getPath().toString();
        if (path.startsWith("/api/user")) {
            return new RateLimitConfig(10, 20, 5);
        } else if (path.startsWith("/api/product")) {
            return new RateLimitConfig(5, 10, 3);
        }
        return null;
    }
    
    private String generateKey(ServerHttpRequest request, RateLimitConfig config) {
        String path = request.getPath().toString();
        String method = request.getMethodValue();
        String userAgent = request.getHeaders().getFirst("User-Agent");
        
        return DigestUtils.md5DigestAsHex((path + method + userAgent).getBytes());
    }
    
    @Override
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE - 100;
    }
}

限流配置类

public class RateLimitConfig {
    
    private int replenishRate;  // 每秒补充令牌数
    private int burst;          // 桶容量
    private int limit;          // 单位时间内的请求限制
    
    public RateLimitConfig() {}
    
    public RateLimitConfig(int replenishRate, int burst, int limit) {
        this.replenishRate = replenishRate;
        this.burst = burst;
        this.limit = limit;
    }
    
    // getter和setter方法
    public int getReplenishRate() { return replenishRate; }
    public void setReplenishRate(int replenishRate) { this.replenishRate = replenishRate; }
    
    public int getBurst() { return burst; }
    public void setBurst(int burst) { this.burst = burst; }
    
    public int getLimit() { return limit; }
    public void setLimit(int limit) { this.limit = limit; }
}

服务熔断机制

Hystrix熔断器集成

@Component
public class CircuitBreakerConfig {
    
    @Bean
    public ReactorLoadBalancer<ReactiveServiceInstance> reactorLoadBalancer(
            Environment environment, 
            ServiceInstanceListSupplier serviceInstanceListSupplier) {
        String name = environment.getProperty("spring.cloud.loadbalancer.configurations", "zone-aware");
        return new RoundRobinLoadBalancer(serviceInstanceListSupplier, name);
    }
    
    @Bean
    public Customizer<ReactiveResilience4JCircuitBreakerFactory> circuitBreakerCustomizer() {
        return factory -> {
            factory.configureDefault(id -> new CircuitBreakerConfigBuilder()
                .circuitBreakerConfig(CircuitBreakerConfig.custom()
                    .failureRateThreshold(50)
                    .waitDurationInOpenState(Duration.ofSeconds(30))
                    .slidingWindowSize(100)
                    .slidingWindowType(CircuitBreakerConfig.SlidingWindowType.COUNT_BASED)
                    .permittedNumberOfCallsInHalfOpenState(10)
                    .build())
                .timeLimiterConfig(TimeLimiterConfig.custom()
                    .timeoutDuration(Duration.ofSeconds(10))
                    .build())
                .build());
        };
    }
}

自定义熔断过滤器

@Component
public class CircuitBreakerFilter implements GatewayFilter, Ordered {
    
    private static final Logger log = LoggerFactory.getLogger(CircuitBreakerFilter.class);
    
    @Autowired
    private ReactiveResilience4JCircuitBreakerFactory circuitBreakerFactory;
    
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        String routeId = getRouteId(exchange);
        
        if (routeId == null || routeId.isEmpty()) {
            return chain.filter(exchange);
        }
        
        CircuitBreaker circuitBreaker = circuitBreakerFactory.create(routeId);
        
        // 创建熔断器包装的响应
        return Mono.fromCallable(() -> {
            try {
                return chain.filter(exchange).then();
            } catch (Exception e) {
                log.error("服务调用异常", e);
                throw new RuntimeException(e);
            }
        })
        .transformDeferred(circuitBreaker::run)
        .onErrorResume(throwable -> {
            log.warn("熔断器触发: {}", throwable.getMessage());
            ServerHttpResponse response = exchange.getResponse();
            response.setStatusCode(HttpStatus.SERVICE_UNAVAILABLE);
            return response.setComplete();
        });
    }
    
    private String getRouteId(ServerWebExchange exchange) {
        Route route = exchange.getAttribute(GatewayFilterChain.class.getName() + ".route");
        return route != null ? route.getId() : null;
    }
    
    @Override
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE - 50;
    }
}

熔断降级处理

@RestController
@RequestMapping("/fallback")
public class FallbackController {
    
    private static final Logger log = LoggerFactory.getLogger(FallbackController.class);
    
    @GetMapping("/user")
    public ResponseEntity<String> userFallback() {
        log.warn("用户服务熔断降级");
        return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE)
                .body("用户服务暂时不可用,请稍后重试");
    }
    
    @GetMapping("/order")
    public ResponseEntity<String> orderFallback() {
        log.warn("订单服务熔断降级");
        return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE)
                .body("订单服务暂时不可用,请稍后重试");
    }
    
    @GetMapping("/product")
    public ResponseEntity<String> productFallback() {
        log.warn("商品服务熔断降级");
        return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE)
                .body("商品服务暂时不可用,请稍后重试");
    }
}

统一认证与授权

JWT认证过滤器实现

@Component
public class AuthFilter implements GatewayFilter, Ordered {
    
    private static final Logger log = LoggerFactory.getLogger(AuthFilter.class);
    
    @Value("${jwt.secret}")
    private String secret;
    
    @Value("${jwt.expiration}")
    private long expiration;
    
    @Value("${auth.skip-urls:}")
    private List<String> skipUrls;
    
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        
        // 跳过认证的路径
        if (shouldSkipAuthentication(request)) {
            return chain.filter(exchange);
        }
        
        // 获取JWT Token
        String token = getTokenFromRequest(request);
        if (token == null || token.isEmpty()) {
            return unauthorizedResponse(response);
        }
        
        // 验证Token
        try {
            Claims claims = Jwts.parser()
                    .setSigningKey(secret)
                    .parseClaimsJws(token)
                    .getBody();
            
            // 检查Token是否过期
            if (claims.getExpiration().before(new Date())) {
                return unauthorizedResponse(response);
            }
            
            // 验证Token是否被撤销
            String redisKey = "jwt:blacklist:" + token;
            Boolean isBlacklisted = redisTemplate.hasKey(redisKey);
            if (Boolean.TRUE.equals(isBlacklisted)) {
                return unauthorizedResponse(response);
            }
            
            // 将用户信息添加到请求头
            ServerHttpRequest mutatedRequest = request.mutate()
                    .header("X-User-ID", claims.getSubject())
                    .header("X-User-Roles", (String) claims.get("roles"))
                    .build();
            
            ServerWebExchange mutatedExchange = exchange.mutate()
                    .request(mutatedRequest)
                    .build();
            
            return chain.filter(mutatedExchange);
            
        } catch (Exception e) {
            log.error("Token验证失败", e);
            return unauthorizedResponse(response);
        }
    }
    
    private String getTokenFromRequest(ServerHttpRequest request) {
        String bearerToken = request.getHeaders().getFirst("Authorization");
        if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
            return bearerToken.substring(7);
        }
        return null;
    }
    
    private boolean shouldSkipAuthentication(ServerHttpRequest request) {
        String path = request.getPath().toString();
        return skipUrls.stream()
                .anyMatch(skipUrl -> path.startsWith(skipUrl));
    }
    
    private Mono<Void> unauthorizedResponse(ServerHttpResponse response) {
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        String body = "{\"code\":401,\"message\":\"未授权访问\"}";
        DataBuffer buffer = response.bufferFactory().wrap(body.getBytes(StandardCharsets.UTF_8));
        return response.writeWith(Mono.just(buffer));
    }
    
    @Override
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE - 200;
    }
}

JWT工具类

@Component
public class JwtUtil {
    
    private static final Logger log = LoggerFactory.getLogger(JwtUtil.class);
    
    @Value("${jwt.secret}")
    private String secret;
    
    @Value("${jwt.expiration}")
    private long expiration;
    
    /**
     * 生成JWT Token
     */
    public String generateToken(UserDetails userDetails) {
        Date now = new Date();
        Date expiryDate = new Date(now.getTime() + expiration);
        
        return Jwts.builder()
                .setSubject(userDetails.getUsername())
                .setIssuedAt(new Date())
                .setExpiration(expiryDate)
                .claim("roles", userDetails.getAuthorities().stream()
                        .map(GrantedAuthority::getAuthority)
                        .collect(Collectors.toList()))
                .signWith(SignatureAlgorithm.HS512, secret)
                .compact();
    }
    
    /**
     * 验证JWT Token
     */
    public boolean validateToken(String token) {
        try {
            Jwts.parser().setSigningKey(secret).parseClaimsJws(token);
            return true;
        } catch (Exception e) {
            log.error("Token验证失败", e);
            return false;
        }
    }
    
    /**
     * 从Token中获取用户名
     */
    public String getUsernameFromToken(String token) {
        return getClaimFromToken(token, Claims::getSubject);
    }
    
    /**
     * 获取Token过期时间
     */
    public Date getExpirationDateFromToken(String token) {
        return getClaimFromToken(token, Claims::getExpiration);
    }
    
    private <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = getAllClaimsFromToken(token);
        return claimsResolver.apply(claims);
    }
    
    private Claims getAllClaimsFromToken(String token) {
        return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
    }
    
    /**
     * 撤销Token(加入黑名单)
     */
    public void revokeToken(String token, long timeout) {
        String redisKey = "jwt:blacklist:" + token;
        redisTemplate.opsForValue().set(redisKey, "revoked", timeout, TimeUnit.MILLISECONDS);
    }
}

认证服务接口

@RestController
@RequestMapping("/api/auth")
public class AuthController {
    
    private static final Logger log = LoggerFactory.getLogger(AuthController.class);
    
    @Autowired
    private UserService userService;
    
    @Autowired
    private JwtUtil jwtUtil;
    
    @PostMapping("/login")
    public ResponseEntity<?> login(@RequestBody LoginRequest request) {
        try {
            User user = userService.authenticate(request.getUsername(), request.getPassword());
            
            if (user != null) {
                String token = jwtUtil.generateToken(new UserPrincipal(user));
                return ResponseEntity.ok(new AuthResponse(token, "Bearer"));
            } else {
                return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
                        .body("用户名或密码错误");
            }
        } catch (Exception e) {
            log.error("登录失败", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body("登录异常");
        }
    }
    
    @PostMapping("/register")
    public ResponseEntity<?> register(@RequestBody RegisterRequest request) {
        try {
            User user = userService.register(request.getUsername(), request.getPassword());
            String token = jwtUtil.generateToken(new UserPrincipal(user));
            return ResponseEntity.ok(new AuthResponse(token, "Bearer"));
        } catch (Exception e) {
            log.error("注册失败", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body("注册异常");
        }
    }
    
    @PostMapping("/logout")
    public ResponseEntity<?> logout(HttpServletRequest request) {
        String token = getTokenFromRequest(request);
        if (token != null) {
            jwtUtil.revokeToken(token, 3600000); // 1小时
        }
        return ResponseEntity.ok("退出成功");
    }
    
    private String getTokenFromRequest(HttpServletRequest request) {
        String bearerToken = request.getHeader("Authorization");
        if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
            return bearerToken.substring(7);
        }
        return null;
    }
}

// 请求参数类
public class LoginRequest {
    private String username;
    private String password;
    
    // getter和setter方法
    public String getUsername() { return username; }
    public void setUsername(String username) { this.username = username; }
    public String getPassword() { return password; }
    public void setPassword(String password) { this.password = password; }
}

public class AuthResponse {
    private String
相关推荐
广告位招租

相似文章

    评论 (0)

    0/2000