Spring Gateway自定义认证过滤器实现微服务鉴权咨询
嘿,我来帮你搞定这个Spring Gateway的JWT鉴权问题!自定义GatewayFilterFactory确实是官方推荐的标准方案,咱们一步步来实现:
实现思路与具体代码
1. 自定义JWT认证过滤器工厂
我们需要创建一个过滤器,负责从请求头提取JWT、调用认证服务验证、根据结果决定放行或返回404。
首先定义过滤器的配置类(用来传递认证服务地址等参数):
public class JwtAuthFilterConfig { private String authServiceUrl; // 构造器、Getter、Setter public JwtAuthFilterConfig(String authServiceUrl) { this.authServiceUrl = authServiceUrl; } public String getAuthServiceUrl() { return authServiceUrl; } }
然后实现过滤器工厂:
@Component public class JwtAuthenticationFilterFactory extends AbstractGatewayFilterFactory<JwtAuthFilterConfig> { private final WebClient webClient; // 注入WebClient.Builder,自动支持负载均衡 public JwtAuthenticationFilterFactory(WebClient.Builder webClientBuilder) { super(JwtAuthFilterConfig.class); this.webClient = webClientBuilder.build(); } @Override public GatewayFilter apply(JwtAuthFilterConfig config) { return (exchange, chain) -> { // 1. 从请求头获取Authorization令牌 String authHeader = exchange.getRequest().getHeaders().getFirst(HttpHeaders.AUTHORIZATION); if (authHeader == null || !authHeader.startsWith("Bearer ")) { return build404Response(exchange, "Missing or invalid authentication token"); } String jwtToken = authHeader.substring(7); // 去掉"Bearer "前缀 // 2. 调用认证服务的验证接口(假设认证服务提供POST /auth/validate接口) return webClient.post() .uri(config.getAuthServiceUrl() + "/auth/validate") .bodyValue(jwtToken) .retrieve() .bodyToMono(Boolean.class) .flatMap(isValid -> { if (Boolean.TRUE.equals(isValid)) { // 验证通过,放行请求到目标微服务 return chain.filter(exchange); } else { // 验证失败,返回404 return build404Response(exchange, "Invalid JWT token"); } }) .onErrorResume(e -> { // 调用认证服务出错时,也返回404 return build404Response(exchange, "Authentication service unavailable"); }); }; } // 构建404错误响应 private Mono<Void> build404Response(ServerWebExchange exchange, String message) { ServerHttpResponse response = exchange.getResponse(); response.setStatusCode(HttpStatus.NOT_FOUND); response.getHeaders().setContentType(MediaType.APPLICATION_JSON); String jsonBody = String.format("{\"error\": \"%s\"}", message); DataBuffer buffer = response.bufferFactory().wrap(jsonBody.getBytes(StandardCharsets.UTF_8)); return response.writeWith(Mono.just(buffer)); } // 配置快捷参数,方便在路由中直接使用 @Override public List<String> shortcutFieldOrder() { return Collections.singletonList("authServiceUrl"); } }
2. 在路由配置中使用过滤器
现在把这个过滤器应用到你的受保护微服务路由上:
// 假设你的负载均衡服务名定义如下 private static final String LOAD_BALANCED_AUTHENTICATION_SERVICE = "lb://authentication-service"; private static final String LOAD_BALANCED_IMAGES_SERVICE = "lb://images-service"; private static final String LOAD_BALANCED_INBOX_SERVICE = "lb://inbox-service"; // 注入WebClient.Builder和自定义过滤器工厂 @Autowired private WebClient.Builder webClientBuilder; @Autowired private JwtAuthenticationFilterFactory jwtAuthFilterFactory; // 路由配置 @Bean public RouteLocator customRouteLocator(RouteLocatorBuilder builder) { return builder.routes() .route(route -> route.path("/auth/**") .uri(LOAD_BALANCED_AUTHENTICATION_SERVICE) .id("authentication-service")) .route(route -> route.path("/images/**") .filters(f -> f.filter(jwtAuthFilterFactory.apply( new JwtAuthFilterConfig(LOAD_BALANCED_AUTHENTICATION_SERVICE)))) .uri(LOAD_BALANCED_IMAGES_SERVICE) .id("images-service")) .route(route -> route.path("/inbox/**") .filters(f -> f.filter(jwtAuthFilterFactory.apply( new JwtAuthFilterConfig(LOAD_BALANCED_AUTHENTICATION_SERVICE)))) .uri(LOAD_BALANCED_INBOX_SERVICE) .id("inbox-service")) .build(); }
或者利用shortcutFieldOrder的配置,用更简洁的方式调用过滤器:
.route(route -> route.path("/images/**") .filters(f -> f.jwtAuthentication(LOAD_BALANCED_AUTHENTICATION_SERVICE)) .uri(LOAD_BALANCED_IMAGES_SERVICE) .id("images-service"))
3. 认证服务配合
确保你的认证服务提供一个验证JWT的接口,比如:
@PostMapping("/auth/validate") public ResponseEntity<Boolean> validateToken(@RequestBody String token) { // 这里实现JWT的验证逻辑,比如解析令牌、检查签名和过期时间等 boolean isValid = jwtUtil.validateToken(token); return ResponseEntity.ok(isValid); }
关键细节说明
- WebClient的使用:因为Spring Gateway基于WebFlux响应式框架,所以用WebClient调用认证服务比RestTemplate更合适,而且它自动支持Spring Cloud的负载均衡。
- 请求终止与放行:通过
build404Response直接返回响应,终止请求链;验证通过则调用chain.filter(exchange)放行请求到目标微服务。 - 错误处理:调用认证服务失败时(比如服务宕机),也返回404,符合你的需求。
内容的提问来源于stack exchange,提问作者user805703




