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

Spring Security 6+Opaque Token认证异常返回逻辑优化问询

Spring Security 6 + Opaque Token 异常处理方案

一、自定义认证服务器不可达异常

先定义专属异常类,用来标记「认证服务器连接失败/超时」的场景,方便后续精准区分处理:

public class IntrospectionServerUnavailableException extends AuthenticationException {
    public IntrospectionServerUnavailableException(String message, Throwable cause) {
        super(message, cause);
    }
}

二、包装OpaqueTokenIntrospector捕获连接异常

自定义一个Introspector实现类,包装原有的Nimbus实现,在令牌校验过程中捕获RestTemplate抛出的连接超时、IO异常等,转换成上面的自定义异常:

@Component
public class CustomOpaqueTokenIntrospector implements OpaqueTokenIntrospector {

    private final OpaqueTokenIntrospector delegate;

    public CustomOpaqueTokenIntrospector(@Value("${spring.security.oauth2.resourceserver.opaque-token.introspection-uri}") String introspectionUrl,
                                         @Value("${spring.security.oauth2.resourceserver.opaque-token.client-id}") String clientId,
                                         @Value("${spring.security.oauth2.resourceserver.opaque-token.client-secret}") String clientSecret,
                                         @Value("${introspection.connection-timeout:5}") int connectionTimeout,
                                         @Value("${introspection.read-timeout:5}") int readTimeout) {
        RestOperations restOperations = new RestTemplateBuilder()
                .basicAuthentication(clientId, clientSecret)
                .setConnectTimeout(Duration.ofSeconds(connectionTimeout))
                .setReadTimeout(Duration.ofSeconds(readTimeout))
                .build();
        this.delegate = new NimbusOpaqueTokenIntrospector(introspectionUrl, restOperations);
    }

    @Override
    public OAuth2AuthenticatedPrincipal introspect(String token) {
        try {
            return delegate.introspect(token);
        } catch (OAuth2IntrospectionException e) {
            // 检查异常根源是否为连接/超时类异常
            Throwable rootCause = e.getCause();
            if (rootCause instanceof ConnectTimeoutException || 
                rootCause instanceof SocketTimeoutException || 
                rootCause instanceof IOException) {
                throw new IntrospectionServerUnavailableException("认证服务器连接失败", rootCause);
            }
            // 其他认证相关异常(如token无效、过期)继续抛出原异常
            throw e;
        }
    }
}

三、配置Spring Security异常处理

通过自定义认证/授权异常处理器,实现不同场景下的响应逻辑:

1. 自定义AuthenticationEntryPoint(处理认证异常)

区分「服务器不可达」和「普通认证失败」,分别返回500和带提示的401响应:

public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
        response.setContentType("application/json;charset=UTF-8");
        if (authException instanceof IntrospectionServerUnavailableException) {
            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            response.getWriter().write("{\"code\":500,\"message\":\"" + authException.getMessage() + "\"}");
        } else {
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            response.getWriter().write("{\"code\":401,\"message\":\"" + authException.getMessage() + "\"}");
        }
    }
}

2. 自定义AccessDeniedHandler(处理授权异常)

返回带提示信息的403响应:

public class CustomAccessDeniedHandler implements AccessDeniedHandler {
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException {
        response.setContentType("application/json;charset=UTF-8");
        response.setStatus(HttpServletResponse.SC_FORBIDDEN);
        response.getWriter().write("{\"code\":403,\"message\":\"权限不足,无法访问该资源\"}");
    }
}

3. 配置SecurityFilterChain

将自定义处理器接入Spring Security配置:

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    private final CustomOpaqueTokenIntrospector customOpaqueTokenIntrospector;

    public SecurityConfig(CustomOpaqueTokenIntrospector customOpaqueTokenIntrospector) {
        this.customOpaqueTokenIntrospector = customOpaqueTokenIntrospector;
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .authorizeHttpRequests(auth -> auth
                        .anyRequest().authenticated()
                )
                .oauth2ResourceServer(oauth2 -> oauth2
                        .opaqueToken(token -> token
                                .introspector(customOpaqueTokenIntrospector)
                        )
                )
                .exceptionHandling(exceptions -> exceptions
                        .authenticationEntryPoint(new CustomAuthenticationEntryPoint())
                        .accessDeniedHandler(new CustomAccessDeniedHandler())
                );
        return http.build();
    }
}

关键说明

  • 异常区分逻辑:通过OAuth2IntrospectionException的根源异常判断是否为连接类故障,这类场景属于服务不可用,返回500;其他如token无效、过期等认证失败场景,返回401。
  • 响应格式:统一使用JSON返回提示信息,可根据业务需求调整JSON结构。
  • 适配Spring Security 6:采用新版本的oauth2ResourceServer().opaqueToken()配置API,符合框架规范。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 09:27:51