如何为已受Keycloak保护的接口添加自定义认证过滤器
问题:Spring Boot + Keycloak 自定义认证过滤器无法触发
我有一个通过KeycloakWebSecurityConfigurerAdapter保护的Spring Boot应用,现在想给部分端点添加另一种自定义认证方式。我已经创建了继承AbstractAuthenticationProcessingFilter和AuthenticationProvider的类并实现了自定义逻辑,之后尝试用下面的代码把自定义过滤器加到HttpSecurity里:
http.addFilterBefore(new VendorSessionAuthorizationFilter(), KeycloakAuthenticationProcessingFilter.class);
预期是自定义过滤器先执行,根据结果再进入Keycloak的过滤器链,但测试调用端点时,过滤器里的attemptAuthentication方法从未触发。感觉不管怎么调用http.addFilterBefore(...),Keycloak过滤器都会优先执行,导致自定义认证用不了。
我的SecurityConfiguration配置如下:
@Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(jsr250Enabled = true, prePostEnabled = true) public class KeycloakSecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { super.configure(http); http.addFilterBefore(new VendorSessionAuthorizationFilter(), KeycloakAuthenticationProcessingFilter.class); http.authenticationProvider(new VendorSessionAuthenticationProvider()); http .cors().and() .csrf().disable() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .sessionAuthenticationStrategy(sessionAuthenticationStrategy()) .and().formLogin().disable() .httpBasic().disable() .logout().disable() .authorizeRequests() .anyRequest().authenticated(); } @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider(); keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper()); auth.authenticationProvider(keycloakAuthenticationProvider); } @Override protected SessionAuthenticationStrategy sessionAuthenticationStrategy() { return new NullAuthenticatedSessionStrategy(); } @Bean public KeycloakConfigResolver KeycloakConfigResolver() { return new KeycloakSpringBootConfigResolver(); } @Bean public FilterRegistrationBean keycloakAuthenticationProcessingFilterRegistrationBean( KeycloakAuthenticationProcessingFilter filter) { FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter); registrationBean.setEnabled(false); return registrationBean; } @Bean public FilterRegistrationBean keycloakPreAuthActionsFilterRegistrationBean( KeycloakPreAuthActionsFilter filter) { FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter); registrationBean.setEnabled(false); return registrationBean; } @Bean public FilterRegistrationBean keycloakAuthenticatedActionsFilterBean( KeycloakAuthenticatedActionsFilter filter) { FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter); registrationBean.setEnabled(false); return registrationBean; } @Bean public FilterRegistrationBean keycloakSecurityContextRequestFilterBean( KeycloakSecurityContextRequestFilter filter) { FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter); registrationBean.setEnabled(false); return registrationBean; } }
原因分析
- 过滤器顺序错误:调用
super.configure(http)后再添加自定义过滤器,此时Keycloak相关过滤器已经被初始化到链中,addFilterBefore无法正确将自定义过滤器插入到目标位置。 - 未指定拦截路径:
AbstractAuthenticationProcessingFilter默认仅处理匹配特定路径的请求,若你的VendorSessionAuthorizationFilter未配置拦截路径,不会触发attemptAuthentication方法。 - 未绑定AuthenticationManager:直接new出的过滤器没有关联Spring的
AuthenticationManager,即使触发也无法完成认证流程。
解决方案
步骤1:正确配置自定义过滤器
将VendorSessionAuthorizationFilter声明为Spring Bean,指定拦截路径并绑定AuthenticationManager:
@Component public class VendorSessionAuthorizationFilter extends AbstractAuthenticationProcessingFilter { // 指定要拦截的端点路径,比如/vendor/** public VendorSessionAuthorizationFilter() { super(new AntPathRequestMatcher("/vendor/**")); } @Override public void setAuthenticationManager(AuthenticationManager authenticationManager) { super.setAuthenticationManager(authenticationManager); } @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException { // 自定义认证逻辑示例 String sessionToken = request.getHeader("X-Vendor-Session"); if (sessionToken == null) { throw new BadCredentialsException("缺失厂商会话令牌"); } VendorSessionAuthenticationToken authRequest = new VendorSessionAuthenticationToken(sessionToken); return getAuthenticationManager().authenticate(authRequest); } }
步骤2:调整过滤器添加顺序
在configure(HttpSecurity http)中先添加自定义过滤器,再调用父类配置,确保自定义过滤器被插入到Keycloak过滤器之前:
@Autowired private VendorSessionAuthorizationFilter vendorSessionAuthorizationFilter; @Autowired private VendorSessionAuthenticationProvider vendorSessionAuthenticationProvider; @Override protected void configure(HttpSecurity http) throws Exception { // 先添加自定义过滤器和认证提供者 http.addFilterBefore(vendorSessionAuthorizationFilter, KeycloakAuthenticationProcessingFilter.class); http.authenticationProvider(vendorSessionAuthenticationProvider); // 再执行父类的Keycloak配置 super.configure(http); http .cors().and() .csrf().disable() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .sessionAuthenticationStrategy(sessionAuthenticationStrategy()) .and().formLogin().disable() .httpBasic().disable() .logout().disable() .authorizeRequests() .antMatchers("/vendor/**").authenticated() .anyRequest().authenticated(); }
步骤3:注册自定义AuthenticationProvider
在configureGlobal方法中添加自定义认证提供者:
@Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider(); keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper()); auth.authenticationProvider(keycloakAuthenticationProvider); // 注册自定义认证提供者 auth.authenticationProvider(vendorSessionAuthenticationProvider); }
内容的提问来源于stack exchange,提问作者Johngaz
相关产品推荐
相关产品推荐

