Вопрос или проблема
В настоящее время я переношу приложение Spring JSF с Spring 5 на последнюю версию Spring 6. Я использую Java 17 с версией spring-boot-starter-parent
3.3.5 и версией Spring Security 6.3.4.
В предыдущей версии у нас была следующая конфигурация в web.xml
для springSecurityFilterChain
:
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>*.jsf</url-pattern>
<url-pattern>*.jsp</url-pattern>
</filter-mapping>
Сейчас мы преобразуем эту конфигурацию в конфигурацию на основе Java, используя Spring Boot. Вот соответствующее определение Bean в нашем классе WebConfig
:
@Bean
public FilterRegistrationBean<DelegatingFilterProxy> springSecurityFilterChain() {
FilterRegistrationBean<DelegatingFilterProxy> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new DelegatingFilterProxy("springSecurityFilterChain"));
registrationBean.addUrlPatterns("*.jsf", "*.jsp");
return registrationBean;
}
Однако я сталкиваюсь со следующей ошибкой во время запуска приложения:
org.springframework.beans.factory.UnsatisfiedDependencyException: Ошибка при создании бина с именем 'springSecurityFilterChain': неудовлетворенная зависимость, выраженная через параметр конструктора 0: Не удалось преобразовать значение аргумента типа [java.util.ArrayList] в требуемый тип [java.util.List]: Не удалось преобразовать значение типа 'java.util.ArrayList' в требуемый тип 'java.util.List'; Невозможно преобразовать значение типа 'org.springframework.boot.web.servlet.FilterRegistrationBean' в требуемый тип 'jakarta.servlet.Filter': не найдены подходящие редакторы или стратегия преобразования
в org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:757) ~[spring-beans-6.1.14.jar:6.1.14]
в org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:237) ~[spring-beans-6.1.14.jar:6.1.14]
в org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1375) ~[spring-beans-6.1.14.jar:6.1.14]
в org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1212) ~[spring-beans-6.1.14.jar:6.1.14]
в org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:562) ~[spring-beans-6.1.14.jar:6.1.14]
в org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522) ~[spring-beans-6.1.14.jar:6.1.14]
в org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:337) ~[spring-beans-6.1.14.jar:6.1.14]
в org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-6.1.14.jar:6.1.14]
в org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:335) ~[spring-beans-6.1.14.jar:6.1.14]
в org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[spring-beans-6.1.14.jar:6.1.14]
в org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:313) ~[spring-beans-6.1.14.jar:6.1.14]
в org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[spring-beans-6.1.14.jar:6.1.14]
в org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:975) ~[spring-beans-6.1.14.jar:6.1.14]
в org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:971) ~[spring-context-6.1.14.jar:6.1.14]
в org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:625) ~[spring-context-6.1.14.jar:6.1.14]
в org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) ~[spring-boot-3.3.5.jar:3.3.5]
в org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754) [spring-boot-3.3.5.jar:3.3.5]
в org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456) [spring-boot-3.3.5.jar:3.3.5]
в org.springframework.boot.SpringApplication.run(SpringApplication.java:335) [spring-boot-3.3.5.jar:3.3.5]
Похоже, что Spring пытается преобразовать FilterRegistrationBean
в jakarta.servlet.Filter
, что и вызывает проблему.
Вопрос:
Как правильно определить Bean springSecurityFilterChain
в Spring 6, чтобы избежать этой ошибки преобразования?
Дополнительная информация:
- Версия Java: 17
- Версия Spring Boot: 3.3.5
- Версия Spring Security: 6.3.4
Любая помощь или руководство будут очень полезны!
Ответ или решение
При миграции вашего приложения с использованием Spring 5 на Spring 6 может возникнуть ряд проблем, связанных с настройками безопасности, особенно в аспектах фильтров Servlet. Рассмотрим ваше конкретное сообщение об ошибке и подход к корректной настройке канала фильтров Spring Security.
Проблема и анализ
Вы столкнулись с исключением UnsatisfiedDependencyException
, которое указывает на ошибку во время создания бина springSecurityFilterChain
. Конкретно, проблема заключается в попытке конвертировать FilterRegistrationBean
в jakarta.servlet.Filter
. Это связано с тем, что в Spring 6 изменились некоторые подходы к конфигурации и обработке фильтров.
Решение
Вместо использования DelegatingFilterProxy
напрямую, мы можем воспользоваться стандартным подходом Spring для настройки безопасности, подстраиваясь под новый синтаксис и стили управления. Настройка фильтров в Spring Boot выглядит несколько иначе по сравнению с традиционным XML.
-
Убедитесь, что ваш проект уже включает зависимости для Spring Security. В файле
pom.xml
илиbuild.gradle
должны быть правильно указаны версии:<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> <version>3.3.5</version> </dependency>
-
Определите
SecurityFilterChain
в вашем классе конфигурации:import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.web.SecurityFilterChain; @Configuration public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeRequests() .requestMatchers("*.jsf", "*.jsp").authenticated() .and() .formLogin() .and() .logout() .permitAll(); return http.build(); } }
Настройка фильтра
При таком подходе вам не нужно отдельно регистрировать springSecurityFilterChain
как фильтр, так как Spring Security автоматически обрабатывает это для вас через SecurityFilterChain
. Вам просто нужно будет настроить, какие URL шаблоны должны быть защищены, и как именно.
Исправление ошибок преобразования
На основании вашего сообщения об ошибке, вам, вероятно, больше не нужно использовать FilterRegistrationBean
для DelegatingFilterProxy
. При правильной конфигурации SecurityFilterChain
будет автоматически зарегистрирован как нужный фильтр.
Подводя итоги
В результате обновления конфигурации безопасности до Java-базированной методологии, вы устраняете ошибку преобразования и используете современные практики управления безопасностью в Spring 6. Этот подход обеспечит не только исправление возникших ошибок, но и новый уровень гибкости и поддержки в будущем.
Если у вас останутся вопросы или возникнут дополнительные проблемы, пожалуйста, не стесняйтесь обращаться за помощью, и мы постараемся вам помочь!