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

如何基于Keycloak与Spring Security实现登录后请求头携带Bearer Token

问题解答

可以实现,通过调整Spring Security配置,结合OAuth2客户端的令牌管理机制,就能在授权成功后自动为后续请求添加Bearer Token到请求头中。

核心思路

Spring作为OAuth2客户端完成授权码流程后,会获取并管理Access Token(而非仅将ID Token存在Cookie)。我们需要通过自定义过滤器,从Spring的令牌管理组件中取出当前用户的Access Token,自动注入到请求的Authorization头中。

具体配置步骤

1. 配置OAuth2授权客户端管理器

先注入OAuth2AuthorizedClientManager,它负责处理客户端令牌的获取、刷新与管理:

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {

    private final String[] anonymousList = {
            "/api/demo/home"
    };

    @Bean
    public OAuth2AuthorizedClientManager authorizedClientManager(
            ClientRegistrationRepository clientRegistrationRepository,
            OAuth2AuthorizedClientService authorizedClientService) {
        // 构建支持授权码模式和令牌刷新的客户端提供者
        OAuth2AuthorizedClientProvider authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
                .authorizationCode()
                .refreshToken()
                .build();

        DefaultOAuth2AuthorizedClientManager authorizedClientManager = new DefaultOAuth2AuthorizedClientManager(
                clientRegistrationRepository, authorizedClientService);
        authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);

        return authorizedClientManager;
    }

2. 自定义Bearer Token请求头过滤器

创建过滤器,从管理器中获取当前用户的Access Token,并注入到请求头:

@Bean
    public FilterRegistrationBean<BearerTokenHeaderFilter> bearerTokenHeaderFilter(OAuth2AuthorizedClientManager authorizedClientManager) {
        FilterRegistrationBean<BearerTokenHeaderFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new BearerTokenHeaderFilter(authorizedClientManager));
        // 设置过滤器优先级,确保在Spring Security核心过滤器前执行
        registrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE + 1);
        // 指定需要添加Token的请求路径
        registrationBean.addUrlPatterns("/api/**");
        return registrationBean;
    }

    public static class BearerTokenHeaderFilter extends OncePerRequestFilter {
        private final OAuth2AuthorizedClientManager authorizedClientManager;

        public BearerTokenHeaderFilter(OAuth2AuthorizedClientManager authorizedClientManager) {
            this.authorizedClientManager = authorizedClientManager;
        }

        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            if (authentication != null) {
                // 构建授权请求,指定Keycloak客户端ID
                OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("keycloak")
                        .principal(authentication)
                        .build();
                OAuth2AuthorizedClient authorizedClient = authorizedClientManager.authorize(authorizeRequest);
                if (authorizedClient != null && authorizedClient.getAccessToken() != null) {
                    String bearerToken = "Bearer " + authorizedClient.getAccessToken().getTokenValue();
                    // 包装请求,注入Authorization头
                    request = new HttpServletRequestWrapper(request) {
                        @Override
                        public String getHeader(String name) {
                            if ("Authorization".equals(name)) {
                                return bearerToken;
                            }
                            return super.getHeader(name);
                        }

                        @Override
                        public Enumeration<String> getHeaders(String name) {
                            if ("Authorization".equals(name)) {
                                return Collections.enumeration(Collections.singletonList(bearerToken));
                            }
                            return super.getHeaders(name);
                        }
                    };
                }
            }
            filterChain.doFilter(request, response);
        }
    }

3. 完善SecurityFilterChain配置

保留原有OAuth2登录配置,确保过滤器正常生效:

@Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.csrf(AbstractHttpConfigurer::disable);
        http.authorizeHttpRequests(
                auth -> auth.requestMatchers(anonymousList)
                        .permitAll()
                        .anyRequest()
                        .authenticated()
                );
        http.oauth2Login(Customizer.withDefaults());
        return http.build();
    }
}

关键说明

  • 过滤器会针对指定路径(如/api/**)自动注入Bearer Token,无需前端手动处理。
  • 确保代码中clientRegistrationId与application.yml里的keycloak客户端ID一致。
  • Spring Security会自动处理令牌过期刷新(需在scope中添加offline_access以获取refresh token)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 12:32:45