如何基于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
相关产品推荐
相关产品推荐

