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

Spring资源服务器Swagger-UI OAuth2授权码模式获取令牌失败

Spring Authorization Server + Swagger-UI 授权码流程CORS问题解决方案

背景

部署了两个Spring应用:

  • Spring Authorization Server:支持authorization_code流程(含PKCE),地址:http://192.168.0.4:8001
  • Spring资源服务器:提供账户CRUD接口,集成OpenAPI/Swagger,接收授权服务器颁发的令牌,地址:http://192.168.0.4:8002

问题现象

Swagger-UI获取授权码后,调用/oauth2/token交换access_token时触发CORS错误:

Access to fetch at 'http://192.168.0.4:8001/oauth2/token' from origin 'http://192.168.0.4:8002' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: Redirect is not allowed for a preflight request.

排查发现:Swagger-UI发起的令牌交换请求未携带Cookie和Basic Auth头,而这两类信息是授权服务器维持状态必需的。Postman测试authorization_code(含PKCE)流程正常,但Swagger-UI无法完成令牌交换。

预期正常的令牌交换请求示例:

curl --location 'http://192.168.100.102:8001/oauth2/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header 'Authorization: Basic Y2xpZW50OnNlY3JldA==' \
--data-urlencode 'grant_type=authorization_code' \
--data-urlencode 'client_id=client' \
--data-urlencode 'redirect_uri=http://192.168.0.4:8002/swagger-ui/oauth2-redirect.html' \
--data-urlencode 'code=S-yW9-fNexepByQEvWnr5MLMSO8YAtX1npBp7VUEF3FAo-YqljVbVGGuf1eIa8kpJnviZXGzsgM59HJlULJc5nHI1blWbcxG7xINm7FpVkcG0zxQKdWBUSxsADy23bKD'

解决方案

1. 修复授权服务器CORS配置

授权服务器的/oauth2/token接口需正确处理跨域预检请求(OPTIONS),禁止在预检请求中返回重定向。添加以下配置:

CORS过滤器配置

@Configuration
public class CorsConfig {
    @Bean
    public CorsFilter corsFilter() {
        CorsConfiguration config = new CorsConfiguration();
        config.addAllowedOrigin("http://192.168.0.4:8002");
        config.setAllowCredentials(true);
        config.addAllowedMethod("*");
        config.addAllowedHeader("*");
        config.addExposedHeader("Authorization");

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/oauth2/**", config);
        source.registerCorsConfiguration("/login/**", config);
        return new CorsFilter(source);
    }
}

Spring Security放行OPTIONS请求

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .cors(Customizer.withDefaults())
            .authorizeHttpRequests(auth -> auth
                .requestMatchers(HttpMethod.OPTIONS, "/oauth2/**").permitAll()
                .anyRequest().authenticated()
            );
        return http.build();
    }
}

2. 调整Swagger-UI OAuth2配置

确保Swagger-UI在令牌交换时携带Basic Auth头和Cookie:

@Configuration
@OpenAPIDefinition(info = @Info(title = "账户API", version = "v1"))
public class OpenApiConfig {
    @Bean
    public OpenAPI customOpenAPI() {
        return new OpenAPI()
            .components(new Components()
                .addSecuritySchemes("oauth2", new SecurityScheme()
                    .type(SecurityScheme.Type.OAUTH2)
                    .flows(new OAuthFlows()
                        .authorizationCode(new OAuthFlow()
                            .authorizationUrl("http://192.168.0.4:8001/oauth2/authorize")
                            .tokenUrl("http://192.168.0.4:8001/oauth2/token")
                            .scopes(new Scopes()
                                .addString("account:read", "读取账户")
                                .addString("account:write", "修改账户")
                            )
                            .extensions(Map.of("useBasicAuthenticationWithAccessCodeGrant", true))
                        )
                    )
                )
            )
            .addSecurityItem(new SecurityRequirement().addList("oauth2"));
    }

    @Bean
    public UiConfiguration uiConfig() {
        return UiConfigurationBuilder.builder()
            .oauth2RedirectUrl("http://192.168.0.4:8002/swagger-ui/oauth2-redirect.html")
            .withCredentials(true)
            .build();
    }
}

3. 检查重定向脚本

使用Swagger-UI默认的oauth2-redirect.html,不要自定义修改请求逻辑,避免阻止Cookie或请求头的携带。

4. 验证PKCE配置

确保授权服务器客户端配置开启PKCE支持:

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig {
    @Bean
    public RegisteredClientRepository registeredClientRepository() {
        RegisteredClient client = RegisteredClient.withId(UUID.randomUUID().toString())
            .clientId("client")
            .clientSecret("{noop}secret")
            .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
            .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
            .redirectUri("http://192.168.0.4:8002/swagger-ui/oauth2-redirect.html")
            .scope("account:read")
            .scope("account:write")
            .clientSettings(ClientSettings.builder().requireProofKey(true).build())
            .build();
        return new InMemoryRegisteredClientRepository(client);
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 23:46:02