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

Spring Boot OAuth2应用同时支持资源服务与Web授权码流的配置问题

解决方案:分离Web安全与资源服务器的过滤器链

这个问题在你当前使用的Spring Boot 1.5.x + Spring Security OAuth 2.0(4.x)版本里完全可以解决,不用升级!核心问题是资源服务器过滤器和Web安全过滤器的顺序冲突:默认情况下@EnableResourceServer的过滤器优先级(order=3)比WebSecurityConfigurerAdapter的默认优先级(order=100)高,导致所有请求都先经过资源服务器处理,Web路径的请求也被判定为需要Bearer Token,从而返回unauthorized,而不是触发授权码流。

我们的解决思路是:

  1. 给Web安全配置设置更高的优先级(比资源服务器低的数字,比如order=2),让Web内容路径的请求先走授权码流的认证链;
  2. 让资源服务器只拦截API路径,且强制无会话,确保Bearer Token调用正常。

步骤1:配置Web安全(授权码流+会话支持)

创建专门的Web安全配置类,指定优先级为2,覆盖Web内容的认证逻辑,同时保留授权码流的跳转能力:

@Configuration
@Order(2) // 优先级高于资源服务器的order=3
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            // 定义需要认证的Web内容路径
            .authorizeRequests()
                .antMatchers("/landing", "/swagger-ui/**", "/swagger-resources/**").authenticated()
                .and()
            // 配置OAuth2授权码流(对接PingFederate)
            .oauth2Login()
                .authorizationEndpoint()
                    .baseUri("/oauth2/authorize") // 自定义授权端点路径
                    .and()
                .redirectionEndpoint()
                    .baseUri("/oauth2/callback/*") // 回调路径,需在PingFederate配置中同步
                .and()
            // 浏览器访问时按需创建会话
            .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);
    }

    // 配置PingFederate的客户端信息
    @Bean
    public ClientRegistrationRepository clientRegistrationRepository() {
        return new InMemoryClientRegistrationRepository(pingFederateClient());
    }

    private ClientRegistration pingFederateClient() {
        return ClientRegistration.withRegistrationId("pingfederate")
            .clientId("your-client-id") // 替换为你的PingFederate客户端ID
            .clientSecret("your-client-secret") // 替换为你的客户端密钥
            .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
            .redirectUriTemplate("{baseUrl}/oauth2/callback/{registrationId}")
            .scope("openid", "profile", "api") // 按需配置PingFederate要求的权限范围
            .authorizationUri("https://your-pingfederate-domain/oauth/authorize") // PingFederate授权端点
            .tokenUri("https://your-pingfederate-domain/oauth/token") // PingFederate令牌端点
            .userInfoUri("https://your-pingfederate-domain/oauth/userinfo") // 用户信息端点(可选)
            .userNameAttributeName(IdTokenClaimNames.SUB)
            .clientName("PingFederate")
            .build();
    }
}

步骤2:配置资源服务器(Bearer Token+无会话)

创建资源服务器配置类,只拦截API路径,且强制无会话:

@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
            // 只拦截API路径
            .requestMatchers()
                .antMatchers("/api/**")
                .and()
            // API请求必须认证
            .authorizeRequests()
                .anyRequest().authenticated()
                .and()
            // API调用不创建会话,避免重定向
            .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }

    // 配置资源服务器的资源ID和令牌验证逻辑
    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources.resourceId("your-api-resource-id") // 替换为PingFederate中配置的资源ID
            .tokenStore(tokenStore());
    }

    // 根据PingFederate的令牌类型选择TokenStore:
    // 如果是JWT令牌,用JwtTokenStore;如果是引用令牌,用RemoteTokenServices
    @Bean
    public TokenStore tokenStore() {
        // 示例:JWT令牌配置
        return new JwtTokenStore(jwtAccessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        // 替换为PingFederate的公钥,用于验证JWT签名
        converter.setVerifierKey("-----BEGIN PUBLIC KEY-----\n你的公钥内容\n-----END PUBLIC KEY-----");
        return converter;
    }

    // 如果是引用令牌,替换为RemoteTokenServices配置:
    /*
    @Bean
    public RemoteTokenServices remoteTokenServices() {
        RemoteTokenServices services = new RemoteTokenServices();
        services.setCheckTokenEndpointUrl("https://your-pingfederate-domain/oauth/check_token");
        services.setClientId("your-client-id");
        services.setClientSecret("your-client-secret");
        return services;
    }
    */
}

关键注意事项

  • 路径隔离:确保Web内容路径(如/landing、Swagger相关)和API路径(如/api/**)没有重叠,避免过滤器链冲突。
  • 令牌验证适配:根据PingFederate发放的令牌类型(JWT或引用令牌)选择对应的TokenStore实现,配置错误会导致令牌验证失败。
  • PingFederate配置同步:确保PingFederate客户端的回调URL、权限范围等与代码中的配置一致,否则授权码流会失败。

测试验证

  1. 浏览器访问Web内容:访问http://localhost:8080/landing,会自动跳转到PingFederate登录页面,登录后建立会话,可正常访问Web内容和Swagger文档。
  2. curl调用API:执行curl -H "Authorization: Bearer your-access-token" http://localhost:8080/api/your-endpoint,无需会话,直接返回API响应。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 09:27:45