0

I need to upgrade Spring Boot, Spring Security, and JDK from 11 to 17

To perform this upgrade, I had to replace javax.servlet.http.* with jakarta.servlet.http.*

The problem I am running into is the compilation of the Line http.addFilterBefore(new MySecurityFilter(), ChannelProcessingFilter.class);

It gives me the compilation error WebSecurityConfig.java:[47,85] incompatible types: java.lang.Class<org.springframework.security.web.access.channel.ChannelProcessingFilter> cannot be converted to java.lang.Class<? extends jakarta.servlet.Filter>

If I do NOT specify this line, the inbuilt security runs for all /api/** requests since they contain the Bearer token. I want to authenticate them myself using MySecurityFilter

Any idea how to fix this since I'm not sure what class in the Jakarta library could be used as a replacement for the ChannelProcessingFilter?

I'll be grateful for any help

// OLD CODE
package com.my.server.security;

import com.my.server.security.filter.MySecurityFilter;
import org.keycloak.adapters.springsecurity.KeycloakSecurityComponents;
import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider;
import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter;
import org.keycloak.adapters.springsecurity.filter.AdapterStateCookieRequestMatcher;
import org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticationProcessingFilter;
import org.keycloak.adapters.springsecurity.filter.QueryParamPresenceRequestMatcher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.web.access.channel.ChannelProcessingFilter;
import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;

import javax.servlet.http.HttpServletRequest;

@Configuration
@EnableWebSecurity
@ComponentScan(basePackageClasses = KeycloakSecurityComponents.class)
class WebSecurityConfig extends KeycloakWebSecurityConfigurerAdapter {

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) {
        KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
        keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
        auth.authenticationProvider(keycloakAuthenticationProvider);
    }

    @Bean
    @Override
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
        return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
    }

    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);
        http.cors().and().csrf().disable();

        http.authorizeRequests()
                .antMatchers("/echo/**", "/api/**").permitAll()
                .antMatchers("/js/**", "/css/**", "/img/**").permitAll()
                .antMatchers("/admin/**").hasAnyRole("USER")
                .anyRequest().authenticated();

        http.addFilterBefore(new MySecurityFilter(), ChannelProcessingFilter.class);
    }

    @Bean
    @Override
    protected KeycloakAuthenticationProcessingFilter keycloakAuthenticationProcessingFilter() throws Exception {
        RequestMatcher requestMatcher =
                new OrRequestMatcher(
                        new AntPathRequestMatcher("/sso/login"),
                        new IgnoreKeycloakProcessingFilterRequestMatcher(), // verify Bearer in MySecurityFilter
                        new QueryParamPresenceRequestMatcher("access_token"),
                        new AdapterStateCookieRequestMatcher()
                );
        return new KeycloakAuthenticationProcessingFilter(authenticationManagerBean(), requestMatcher);
    }

    private class IgnoreKeycloakProcessingFilterRequestMatcher implements RequestMatcher {
        IgnoreKeycloakProcessingFilterRequestMatcher() {}
        public boolean matches(HttpServletRequest request) {
            String authorizationHeaderValue = request.getHeader("Authorization");
            return authorizationHeaderValue != null && !authorizationHeaderValue.startsWith("Bearer ");
        }
    }
}



import org.springframework.web.filter.GenericFilterBean;

public class MySecurityFilter extends GenericFilterBean {
    ...
}
// NEW CODE
package com.my.server.security;

import com.my.server.security.filter.MySecurityFilter;
import jakarta.servlet.http.HttpServletRequest;
import org.keycloak.adapters.springsecurity.KeycloakSecurityComponents;
import org.keycloak.adapters.springsecurity.filter.AdapterStateCookieRequestMatcher;
import org.keycloak.adapters.springsecurity.filter.QueryParamPresenceRequestMatcher;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.channel.ChannelProcessingFilter;
import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;

@Configuration
@EnableWebSecurity
@ComponentScan(basePackageClasses = KeycloakSecurityComponents.class)
public class WebSecurityConfig {

    @Bean
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
        return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.cors().and().csrf().disable();

        http.authorizeRequests()
                .requestMatchers("/echo/**", "/api/**").permitAll()
                .requestMatchers("/js/**", "/css/**", "/img/**").permitAll()
                .requestMatchers("/admin/**").hasAnyRole("USER")
                .anyRequest().authenticated();

        http.addFilterBefore(new MySecurityFilter(), ChannelProcessingFilter.class);

        return http.build();
    }

    @Bean
    public WebSecurityCustomizer webSecurityCustomizer() throws Exception {
        return (web) -> web.ignoring()
                .requestMatchers(
                        new OrRequestMatcher(
                                new AntPathRequestMatcher("/sso/login"),
                                new IgnoreKeycloakProcessingFilterRequestMatcher(),
                                new QueryParamPresenceRequestMatcher("access_token"),
                                new AdapterStateCookieRequestMatcher()
                        )
                );
    }

    private class IgnoreKeycloakProcessingFilterRequestMatcher implements RequestMatcher {
        IgnoreKeycloakProcessingFilterRequestMatcher() {}
        public boolean matches(HttpServletRequest request) {
            String authorizationHeaderValue = request.getHeader("Authorization");
            return authorizationHeaderValue != null && !authorizationHeaderValue.startsWith("Bearer ");
        }
    }
}



import jakarta.servlet.Filter;
import jakarta.servlet.http.HttpFilter;

public class MySecurityFilter extends HttpFilter implements Filter {
    ...
}
Jared
  • 73
  • 6
  • You are upgrading too much at once. Your error indicates you haven't uypgraded spring security (which should be upgraded automatically when you upgrade Spring Boot, why are you managing that separately)? – M. Deinum Aug 29 '23 at 18:43
  • I am upgrading Spring boot and Spring security to versions 3.1.3 and 6.1.3 respectively – Jared Aug 29 '23 at 20:36
  • 1
    You aren't as it clearly indicates that the filter used (which is from Spring Security) is still using older api's . Also, as mentioned, you shouldn't manage your spring security dependency separately. Please add you `pom.xml` to the question. – M. Deinum Aug 30 '23 at 07:03
  • what is the purpose of `MySecurityFilter` why do you need a custom filter? – Toerktumlare Aug 30 '23 at 07:39
  • I found the fix. I need to use `http.addFilterBefore(new MySecurityFilter();` instead. I guess this is a new API introduced since I don't recollect it in the previous release! I solved the compilation, but am running into a different runtime error! You're right about my using older APIs. I identified that one of the dependencies is using javax.servlet instead of jakarta.servlet and our upgrade is blocked by that library! – Jared Aug 30 '23 at 18:07

0 Answers0