DefaultSecurityFilterChain инициализируется два раза при запуске spring-boot-starter-security со свего самописного стартера

Прошу помощи, до этого еще не писал свои собственнве стартеры. Я решил вынести кофигурацию spring security в отдельный старетр что б мог его использовать в других микросервисах. Теперь при запуске проекта с использованием моего arigato-security-starter в качестве зависимости в логах видно что DefaultSecurityFilterChain инициализируется два раза. По этой причине я так понимаю что когда я обращаюсь на защищенный endpoint с токеном, я не попадаю на свой самописный фильтр CustomAuthenticationFilter и всегда получаю статус ошибки 401 Unauthorized.

Вот краткий код и настройка моего стартера

2024-04-20T13:52:22.634+05:00  INFO 16540 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 610 ms
2024-04-20T13:52:22.920+05:00  INFO 16540 --- [           main] o.s.s.web.DefaultSecurityFilterChain     : Will secure any request with [org.springframework.security.web.session.DisableEncodeUrlFilter@4b765e92, org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@4665428b, org.springframework.security.web.context.SecurityContextHolderFilter@ccd341d, org.springframework.security.web.header.HeaderWriterFilter@63f40ca0, org.springframework.web.filter.CorsFilter@4a70d302, org.springframework.security.web.csrf.CsrfFilter@59636c47, org.springframework.security.web.authentication.logout.LogoutFilter@2415e4c7, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@6b63abdc, org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter@16b3c905, org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter@7fd99443, org.springframework.security.web.authentication.www.BasicAuthenticationFilter@241fbec, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@6ee5f485, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@20d19f2c, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@622d7e4, org.springframework.security.web.access.ExceptionTranslationFilter@324b6a56, org.springframework.security.web.access.intercept.AuthorizationFilter@6b37df8e]
2024-04-20T13:52:22.924+05:00  INFO 16540 --- [           main] o.s.s.web.DefaultSecurityFilterChain     : Will secure any request with [org.springframework.security.web.session.DisableEncodeUrlFilter@587c5c1, org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@528c8c1, org.springframework.security.web.context.SecurityContextHolderFilter@29f3c438, org.springframework.security.web.header.HeaderWriterFilter@2abafa97, ru.arigato.securitystarter.config.filters.CustomAuthenticationFilter@7fb53256, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@5460edd3, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@5dbbb292, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@1f78d415, org.springframework.security.web.session.SessionManagementFilter@40717ed, org.springframework.security.web.access.ExceptionTranslationFilter@724bf25f, org.springframework.security.web.access.intercept.AuthorizationFilter@565aa4ac]
2024-04-20T13:52:22.962+05:00  INFO 16540 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port 8080 (http) with context path ```
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.5</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <properties>
        <java.version>21</java.version>
        <maven.compiler.source>${java.version}</maven.compiler.source>
        <maven.compiler.target>${java.version}</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
    </dependencies>
@Configuration
@AllArgsConstructor
@EnableWebSecurity
@EnableConfigurationProperties(SecurityProperties.class)
@ComponentScan("ru.arigato.securitystarter.config")
public class SecurityConfig {

    private final SecurityProperties securityProperties;
    private final CustomAuthenticationFilter customAuthenticationFilter;

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        return http
                .csrf(AbstractHttpConfigurer::disable)
                .cors(AbstractHttpConfigurer::disable)
                .logout(AbstractHttpConfigurer::disable)
                .formLogin(AbstractHttpConfigurer::disable)
                .sessionManagement(sessionManagement -> sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .addFilterAt(customAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
                .authorizeHttpRequests(authorizeRequests -> {
                    authorizeRequests.anyRequest().authenticated();
                })
                .build();
    }

}
@Component
@AllArgsConstructor
public class CustomAuthenticationFilter extends OncePerRequestFilter {

    private final CustomAuthenticationManager customAuthenticationManager;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        //какой то код.....
        Authentication authentication = customAuthenticationManager.authenticate(jwtAuthentication);

    }
}
@Component
@AllArgsConstructor
public class CustomAuthenticationManager implements AuthenticationManager {

    private final List<AuthenticationProvider> provider;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
//....
    }}
@Component
public class JwtAuthenticationProvider implements AuthenticationProvider {

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
//......
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return JwtAuthentication.class.equals(authentication);
    }

}
resources
  .META-INF
     .spring
         .org.springframework.boot.autoconfigure.AutoConfiguration.imports
ru.arigato.securitystarter.config.SecurityConfig

resources
  .META-INF
     org.springframework.boot.env.EnvironmentPostProcessor=ru.arigato.securitystarter.config.EnvPostProcessor

Ответы (2 шт):

Автор решения: Руслан

Не знаю как это работает, но я чисто случаной заметил что если мой путь проекта всместа

 ru.arigato.securitystarter.config

заменить на

dev.arigato.securitystarter.config

то все работает и DefaultSecurityFilterChain инициализируется один раз. И все запроса попадают на мой самописный фильтр.

→ Ссылка
Автор решения: WDeath

Возникла та же проблема. В самописном стартере секьюрити фильтр не подтягивает. Только спустя сутки поиска, нашёл проблему похожую на мою, и вот я здесь.

Так же две цепочки фильтров образуются в логах. Наименование пакетов не могу сделать одинаковым.

Были ли найдены другие решения?

Скорее всего, spring security starter сканируется по пакетам, и для стартера он будет своим, приходиться переопределять. Моё решение, сделал шаблон, который потом в основном проекте делаю бином передавая в конструктор всё нужное

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    private final SecurityTemplate template;

    public SecurityConfig(TokenFilter tokenFilter, ErrorEndpoint errorEndpoint) {
        this.template = new SecurityTemplate(errorEndpoint, tokenFilter);
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
        return template.filterChain(httpSecurity, null);
    }
}
→ Ссылка