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

Spring 6资源服务器无法识别Bearer Access Token的问题排查

问题描述

我正在使用Spring 6.0开发一款支持浏览器和独立客户端访问的应用,采用Keycloak作为OAuth2授权服务器,应用本身作为资源服务器提供数据。
浏览器端访问正常,但独立客户端通过设备授权流或密码授权流从Keycloak获取Access Token后,将其传递给Spring应用时,请求被重定向到Keycloak登录页面,而非验证有效Token。

当前配置

@Configuration
public class OAuth2Config
{

   /**
    * The primary client authentication manager.
    * 
    * @param clientRegistrationRepository
    * @param authorizedClientRepository
    * @return The authorized client manager
    */
   @Bean
   public OAuth2AuthorizedClientManager clientManager( 
         ClientRegistrationRepository clientRegistrationRepository, 
         OAuth2AuthorizedClientRepository authorizedClientRepository )
   {
      // Use the default OAuth client manager - configurations can be made in
      // the properties - and set the built provider.
      //
      DefaultOAuth2AuthorizedClientManager clientManager = new DefaultOAuth2AuthorizedClientManager(
            clientRegistrationRepository, authorizedClientRepository );

      return clientManager;

   }

}

日志信息

2023-01-04T15:30:19.996-06:00 TRACE 9680 --- [nio-8081-exec-4] o.s.security.web.FilterChainProxy        : Trying to match request against DefaultSecurityFilterChain [RequestMatcher=any request, Filters=[org.springframework.security.web.session.DisableEncodeUrlFilter@6c6c93f8, org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@38e88e13, org.springframework.security.web.context.SecurityContextHolderFilter@89178b4, org.springframework.security.web.header.HeaderWriterFilter@20ad64c, org.springframework.security.web.csrf.CsrfFilter@1512efe9, org.springframework.security.web.authentication.logout.LogoutFilter@7fa86ddd, org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter@59509393, org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter@7ec75228, org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter@1d6dc2b8, org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter@3314f179, org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter@3a720ae3, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@64836643, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@42684d86, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@1fe37d27, org.springframework.security.oauth2.client.web.OAuth2AuthorizationCodeGrantFilter@3fc7abf6, org.springframework.security.web.access.ExceptionTranslationFilter@600b3bee, org.springframework.security.web.access.intercept.AuthorizationFilter@565030b7]] (1/1)
2023-01-04T15:30:19.996-06:00 DEBUG 9680 --- [nio-8081-exec-4] o.s.security.web.FilterChainProxy        : Securing GET /users
2023-01-04T15:30:19.996-06:00 TRACE 9680 --- [nio-8081-exec-4] o.s.security.web.FilterChainProxy        : Invoking DisableEncodeUrlFilter (1/17)
:
:
2023-01-04T15:30:20.000-06:00 TRACE 9680 --- [nio-8081-exec-4] o.s.security.web.FilterChainProxy        : Invoking AuthorizationFilter (17/17)
2023-01-04T15:30:25.332-06:00 TRACE 9680 --- [nio-8081-exec-4] estMatcherDelegatingAuthorizationManager : Authorizing SecurityContextHolderAwareRequestWrapper[ org.springframework.security.web.header.HeaderWriterFilter$HeaderWriterRequest@60be86e5]
2023-01-04T15:30:25.332-06:00 TRACE 9680 --- [nio-8081-exec-4] estMatcherDelegatingAuthorizationManager : Checking authorization on SecurityContextHolderAwareRequestWrapper[ org.springframework.security.web.header.HeaderWriterFilter$HeaderWriterRequest@60be86e5] using org.springframework.security.authorization.AuthenticatedAuthorizationManager@3ba6b23
2023-01-04T15:30:25.332-06:00 TRACE 9680 --- [nio-8081-exec-4] w.c.HttpSessionSecurityContextRepository : No HttpSession currently exists
2023-01-04T15:30:25.332-06:00 TRACE 9680 --- [nio-8081-exec-4] .s.s.w.c.SupplierDeferredSecurityContext : Created SecurityContextImpl [Null authentication]
2023-01-04T15:30:25.333-06:00 TRACE 9680 --- [nio-8081-exec-4] .s.s.w.c.SupplierDeferredSecurityContext : Created SecurityContextImpl [Null authentication]
2023-01-04T15:30:25.333-06:00 TRACE 9680 --- [nio-8081-exec-4] o.s.s.w.a.AnonymousAuthenticationFilter  : Set SecurityContextHolder to AnonymousAuthenticationToken [Principal=anonymousUser, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=192.168.0.119, SessionId=null], Granted Authorities=[ROLE_ANONYMOUS]]
2023-01-04T15:30:25.334-06:00 TRACE 9680 --- [nio-8081-exec-4] o.s.s.w.a.ExceptionTranslationFilter     : Sending AnonymousAuthenticationToken [Principal=anonymousUser, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=192.168.0.119, SessionId=null], Granted Authorities=[ROLE_ANONYMOUS]] to authentication entry point since access is denied

解决方案

核心问题是仅配置了OAuth2客户端登录逻辑,未开启资源服务器的Token验证机制。当前过滤器链只有浏览器端OAuth2授权码流的处理过滤器,没有解析验证Bearer Token的组件,导致应用无法识别请求中的Token,将其判定为匿名访问进而触发重定向登录。

需要做以下调整:

1. 添加资源服务器配置

创建或修改Spring Security配置类,同时支持浏览器端OAuth2登录和API端Bearer Token验证:

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            // 区分不同访问路径的认证规则
            .authorizeHttpRequests(auth -> auth
                // API接口要求Bearer Token认证
                .requestMatchers("/api/**").authenticated()
                // 浏览器端路径使用OAuth2登录
                .anyRequest().permitAll()
            )
            // 配置资源服务器,处理JWT格式的Bearer Token
            .oauth2ResourceServer(oauth2 -> oauth2
                .jwt(jwt -> jwt
                    // Keycloak的公钥端点,用于验证Token签名
                    .jwkSetUri("http://你的Keycloak地址/auth/realms/你的领域/protocol/openid-connect/certs")
                )
            )
            // 保留浏览器端的OAuth2登录配置
            .oauth2Login(withDefaults());

        return http.build();
    }

    // 保留原有的OAuth2AuthorizedClientManager配置(若需客户端自动刷新Token则保留)
    @Bean
    public OAuth2AuthorizedClientManager clientManager(
            ClientRegistrationRepository clientRegistrationRepository,
            OAuth2AuthorizedClientRepository authorizedClientRepository) {
        DefaultOAuth2AuthorizedClientManager clientManager = new DefaultOAuth2AuthorizedClientManager(
                clientRegistrationRepository, authorizedClientRepository);
        return clientManager;
    }
}

2. 补充配置文件参数

在application.yml或application.properties中添加资源服务器的必要配置:

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          # 验证Token的发行方,需与Keycloak领域地址一致
          issuer-uri: http://你的Keycloak地址/auth/realms/你的领域

3. 确保客户端请求正确携带Token

独立客户端发送请求时,必须在请求头中添加Authorization字段,格式为:

Authorization: Bearer <你的Access Token>

关键说明

  • 添加oauth2ResourceServer().jwt()后,Spring Security会自动加入BearerTokenAuthenticationFilter,负责解析请求头中的Token并完成验证;
  • jwkSetUri是Keycloak提供的公钥接口,用于确认JWT签名的合法性;
  • issuer-uri用于校验Token的发行来源,防止非法Token被通过验证。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 20:40:18