Spring Reactive环境下OAuth2服务宕机时的自定义错误处理问询
解决方案:自定义OAuth2 JWKS获取异常的错误响应
当OAuth Server宕机导致Api Gateway无法获取JWKS时,默认的异常处理不会将这类错误转换为自定义响应格式。需要通过自定义JWT解码器和异常处理逻辑来实现需求,具体步骤如下:
1. 自定义JWT解码器,捕获JWKS获取异常
默认的jwkSetUri方法抛出的运行时异常不会被AuthenticationEntryPoint捕获,因此需要手动构建JwtDecoder,将JWKS相关异常转换为OAuth2认证异常:
@Bean public JwtDecoder jwtDecoder(@Value("${app.jwk-set-uri:}") String jwksUri) { // 基于Nimbus构建JWT解码器 NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withJwkSetUri(jwksUri).build(); // 添加校验器,捕获JWKS获取异常并转换为OAuth2AuthenticationException jwtDecoder.setJwtValidator(jwt -> { try { return DefaultJwtValidator.instance.validate(jwt); } catch (RuntimeException e) { // 匹配JWKS获取失败的异常场景(网络错误、服务不可达等) if (e.getCause() instanceof ResourceAccessException || e.getMessage().contains("Failed to retrieve JWKS")) { throw new OAuth2AuthenticationException( new OAuth2Error("auth_server_unavailable", "认证服务暂时不可用,请稍后重试", null) ); } throw e; } }); return jwtDecoder; }
2. 修改Security配置,使用自定义解码器
将原配置中jwkSetUri的调用替换为自定义的JwtDecoder:
@RefreshScope @Bean public SecurityWebFilterChain securityWebFilterChain( ServerHttpSecurity http, SecurityProperties securityProperties, JwtDecoder jwtDecoder) { log.info("Updated Public Endpoints: {}", securityProperties.getPublicEndpoints()); var publicEndpoints = securityProperties.getPublicEndpoints(); http .csrf(ServerHttpSecurity.CsrfSpec::disable) .authorizeExchange(exchange -> exchange.anyExchange().authenticated()) .oauth2ResourceServer(oauth2 -> oauth2 .jwt(jwtSpec -> jwtSpec.decoder(jwtDecoder)) .authenticationEntryPoint(authenticationEntryPoint) ) .exceptionHandling(exceptionHandling -> exceptionHandling .authenticationEntryPoint(authenticationEntryPoint) ); return http.build(); }
3. 完善AuthenticationEntryPoint,返回自定义响应
在自定义的ServerAuthenticationEntryPoint中处理OAuth2AuthenticationException,输出自定义错误模型:
@Component public class CustomAuthenticationEntryPoint implements ServerAuthenticationEntryPoint { @Override public Mono<Void> commence(ServerHttpRequest request, ServerHttpResponse response, AuthenticationException authException) { // 设置响应状态码(建议用503表示服务不可用) response.setStatusCode(HttpStatus.SERVICE_UNAVAILABLE); response.getHeaders().setContentType(MediaType.APPLICATION_JSON); // 构建自定义错误模型 CustomErrorResponse errorResponse = new CustomErrorResponse(); errorResponse.setCode("AUTH_SERVER_UNAVAILABLE"); errorResponse.setMessage("认证服务暂时不可用,请稍后重试"); errorResponse.setPath(request.getPath().value()); // 转换为JSON并写入响应 ObjectMapper objectMapper = new ObjectMapper(); try { byte[] jsonBytes = objectMapper.writeValueAsBytes(errorResponse); DataBuffer buffer = response.bufferFactory().wrap(jsonBytes); return response.writeWith(Mono.just(buffer)); } catch (JsonProcessingException e) { return response.setComplete(); } } } // 自定义错误模型类 public class CustomErrorResponse { private String code; private String message; private String path; // Getter和Setter方法 public String getCode() { return code; } public void setCode(String code) { this.code = code; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public String getPath() { return path; } public void setPath(String path) { this.path = path; } }
关键说明
- JWKS获取失败的异常属于运行时异常,必须手动转换为
OAuth2AuthenticationException才能被AuthenticationEntryPoint捕获 - 响应状态码建议使用
503 SERVICE_UNAVAILABLE,更准确反映服务不可用的场景 - 可根据实际需求扩展异常匹配规则,覆盖更多JWKS获取失败的场景
内容的提问来源于stack exchange,提问作者BOOM
相关产品推荐
相关产品推荐

