You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot Security资源服务器授权服务不可用时异常捕获方案

捕获Spring Boot Security资源服务中AuthenticationServiceException的方法

问题场景

已在Spring Boot Security中配置资源服务对接独立授权服务器实现请求授权,但当授权服务器不可用时,无法捕获抛出的AuthenticationServiceException。

相关配置代码

@Configuration
@EnableWebSecurity
@Slf4j
public class AuthIntrospector {

    @Value("${spring.security.oauth2.resourceserver.opaquetoken.introspection-uri}")
    private String introspectionUri;
    @Value("${spring.security.oauth2.resourceserver.opaquetoken.client-id}")
    private String clientId;
    @Value("${spring.security.oauth2.resourceserver.opaquetoken.client-secret}")
    private String clientSecret;

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

        http
                .authorizeHttpRequests(authorize ->
                        authorize.anyRequest().authenticated())
                .oauth2ResourceServer(oauth2 ->
                        oauth2.opaqueToken(opaque -> opaque
                                        .introspector(opaqueTokenIntrospector()))
                )
                .exceptionHandling(exception -> exception
                        .authenticationEntryPoint(authenticationEntryPoint()));
        return http.build();
    }

    @Bean
    public AuthenticationEventPublisher authenticationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        return new DefaultAuthenticationEventPublisher(applicationEventPublisher);
    }

    @Bean
    public AuthenticationEntryPoint authenticationEntryPoint() {
        return new IntrospectionEntryPoint();
    }

    @Bean
    public OpaqueTokenIntrospector opaqueTokenIntrospector() {
            return new SpringOpaqueTokenIntrospector(introspectionUri, clientId, clientSecret);
    }
}

异常栈信息

org.springframework.security.authentication.AuthenticationServiceException: I/O error on POST request for "https://localhost:8085/oauth2/introspect": Connection refused
    at org.springframework.security.oauth2.server.resource.authentication.OpaqueTokenAuthenticationProvider.getOAuth2AuthenticatedPrincipal(OpaqueTokenAuthenticationProvider.java:132) ~[spring-security-oauth2-resource-server-6.1.5.jar:6.1.5]
    ...(省略后续栈信息)

可行的异常捕获方法

方法一:自定义OpaqueTokenIntrospector代理类

通过代理原有的SpringOpaqueTokenIntrospector,在调用令牌introspect方法时直接捕获异常,实现自定义处理逻辑。

@Bean
public OpaqueTokenIntrospector opaqueTokenIntrospector() {
    SpringOpaqueTokenIntrospector delegate = new SpringOpaqueTokenIntrospector(introspectionUri, clientId, clientSecret);
    return (token) -> {
        try {
            return delegate.introspect(token);
        } catch (AuthenticationServiceException e) {
            log.error("授权服务器调用失败: {}", e.getMessage(), e);
            // 可重新抛出自定义异常,或返回标识认证失败的Principal
            throw new RuntimeException("授权服务暂时不可用,请稍后重试");
        }
    };
}

方法二:扩展AuthenticationEntryPoint处理特定异常

在自定义的AuthenticationEntryPoint中,判断异常类型是否为AuthenticationServiceException,针对性返回响应。

public class IntrospectionEntryPoint implements AuthenticationEntryPoint {
    private static final Logger log = LoggerFactory.getLogger(IntrospectionEntryPoint.class);

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
        if (authException instanceof AuthenticationServiceException) {
            log.error("授权服务调用异常", authException);
            response.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write("{\"code\":503,\"message\":\"授权服务暂时不可用,请稍后重试\"}");
            return;
        }
        // 处理其他认证异常
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write("{\"code\":401,\"message\":\"认证失败\"}");
    }
}

方法三:全局异常处理器(@ControllerAdvice)

结合@ControllerAdvice和@ExceptionHandler捕获全局范围内的AuthenticationServiceException,注意该方法仅能捕获进入控制器层后的异常,若异常在过滤器链抛出,需配合AuthenticationEntryPoint使用。

@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    @ExceptionHandler(AuthenticationServiceException.class)
    public ResponseEntity<ErrorResponse> handleAuthServiceException(AuthenticationServiceException e) {
        log.error("授权服务调用失败", e);
        ErrorResponse error = new ErrorResponse(503, "授权服务暂时不可用,请稍后重试");
        return new ResponseEntity<>(error, HttpStatus.SERVICE_UNAVAILABLE);
    }

    static class ErrorResponse {
        private int code;
        private String message;

        public ErrorResponse(int code, String message) {
            this.code = code;
            this.message = message;
        }

        // getter和setter方法
        public int getCode() { return code; }
        public void setCode(int code) { this.code = code; }
        public String getMessage() { return message; }
        public void setMessage(String message) { this.message = message; }
    }
}

内容的提问来源于stack exchange,提问作者Kaif

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 18:00:57