spring.h2.console.enabled: true
๋ก ์ค์ ํ๋ฉด์ Bean์ด ์ ์์ ์ผ๋ก ์์ฑ๋์ง ์๋ ์ด์๊ฐ ๋ฐ์๋์์ด์.
Spring Boot๊ฐ 3์ ๋๋ก ๋ฒ์ ์ด ์
๊ทธ๋ ์ด๋ ๋๋ฉด์ Spring Security ์์กด์ฑ ๋ฒ์ ๋ ํจ๊ป ์ฌ๋ผ๊ฐ์ด์. 2.7.X
๊ธฐ์ค์ผ๋ก Spring Security๋ 5.7.11
๋ฒ์ ์ด์์ง๋ง, Spring Boot 3.1.4
๊ธฐ์ค์ผ๋ก Spring Security๋ 6.1.4
๋ฒ์ ์ ์์กด์ฑ ์ฃผ์
๋ฐ๊ณ ์์ด์.
Spring Boot 2.7.X Dependency Versions
Spring Boot 3.1.4 Dependency Versions
์ด์ ๋ฐ๋ผ Security์๋ ๊ธฐ์กด์ ์ฝ๋๋ค์ด Deprecated๊ฐ ๋ ๊ฒฝ์ฐ๊ฐ ๋ง์ผ๋ฉฐ, Security์ ๊ฐ๋ ์ ๊ฐ์ง๋ง ๋ฉ์๋๋ช ํน์ ์์๋ฐ๋ ๊ตฌ์กฐ ๋ฑ ๋ง์ ๋ถ๋ถ์์ ๋ณํ๊ฐ ์๊ฒผ์ด์. ์ด์ ๊ธฐ์กด 2์ ๋ ๋ฒ์ ์์๋ ์ ์์ ์ผ๋ก ๋์ํ๋ ๊ฒ๋ค๋ 3๋ฒ์ ๋๋ก ๊ฐ๋ฉด์ ์๋กญ๊ฒ ๋ง์ด๊ทธ๋ ์ด์ ํด์ฃผ์ด์ผ ํ๋ ๋ถ๋ถ์ด ์๊ฒผ์ด์.
H2 Console ํ์ฑํ
spring:
h2:
console:
enabled: true
3.1.4
6.1.4
Java 17
Gradle 8.3
@EnableMethodSecurity
@EnableWebSecurity
@Configuration
public class SecurityConfig {
private static final String[] WHITE_LIST = {
"/helloWorld",
};
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.csrf(AbstractHttpConfigurer::disable)
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.headers(header -> header.frameOptions(HeadersConfigurer.FrameOptionsConfig::sameOrigin))
.authorizeHttpRequests(request -> request
.requestMatchers(PathRequest.toH2Console()).permitAll()
.requestMatchers(WHITE_LIST).permitAll()
.anyRequest().authenticated())
.build();
}
}
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration': Unsatisfied dependency expressed through method 'setFilterChains' parameter 0: Error creating bean with name 'securityFilterChain' defined in class path resource [com/demo/springdemo/global/security/SecurityConfig.class]: Failed to instantiate [org.springframework.security.web.SecurityFilterChain]: Factory method 'securityFilterChain' threw exception with message: This method cannot decide whether these patterns are Spring MVC patterns or not. If this endpoint is a Spring MVC endpoint, please use requestMatchers(MvcRequestMatcher); otherwise, please use requestMatchers(AntPathRequestMatcher).
This is because there is more than one mappable servlet in your servlet context: {org.h2.server.web.JakartaWebServlet=[/h2-console/*], org.springframework.web.servlet.DispatcherServlet=[/]}.
For each MvcRequestMatcher, call MvcRequestMatcher#setServletPath to indicate the servlet path.
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.resolveMethodArguments(AutowiredAnnotationBeanPostProcessor.java:875) ~[spring-beans-6.0.12.jar:6.0.12]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:828) ~[spring-beans-6.0.12.jar:6.0.12]
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:145) ~[spring-beans-6.0.12.jar:6.0.12]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:492) ~[spring-beans-6.0.12.jar:6.0.12]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1416) ~[spring-beans-6.0.12.jar:6.0.12]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:597) ~[spring-beans-6.0.12.jar:6.0.12]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:520) ~[spring-beans-6.0.12.jar:6.0.12]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) ~[spring-beans-6.0.12.jar:6.0.12]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-6.0.12.jar:6.0.12]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) ~[spring-beans-6.0.12.jar:6.0.12]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) ~[spring-beans-6.0.12.jar:6.0.12]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:973) ~[spring-beans-6.0.12.jar:6.0.12]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:942) ~[spring-context-6.0.12.jar:6.0.12]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:608) ~[spring-context-6.0.12.jar:6.0.12]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) ~[spring-boot-3.1.4.jar:3.1.4]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:737) ~[spring-boot-3.1.4.jar:3.1.4]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439) ~[spring-boot-3.1.4.jar:3.1.4]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) ~[spring-boot-3.1.4.jar:3.1.4]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1309) ~[spring-boot-3.1.4.jar:3.1.4]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1298) ~[spring-boot-3.1.4.jar:3.1.4]
at com.demo.springdemo.SpringDemoApplication.main(SpringDemoApplication.java:10) ~[main/:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:50) ~[spring-boot-devtools-3.1.4.jar:3.1.4]
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'securityFilterChain' defined in class path resource [com/demo/springdemo/global/security/SecurityConfig.class]: Failed to instantiate [org.springframework.security.web.SecurityFilterChain]: Factory method 'securityFilterChain' threw exception with message: This method cannot decide whether these patterns are Spring MVC patterns or not. If this endpoint is a Spring MVC endpoint, please use requestMatchers(MvcRequestMatcher); otherwise, please use requestMatchers(AntPathRequestMatcher).
org.h2.server.web.JakartaWebServlet
๊ณผ org.springframework.web.servlet.DispatcherServlet
๋ ๊ฐ์ Servlet์ด Servlet Context์ ๋ฑ๋ก๋์ด ์ด๋ค Servlet์ ์ฌ์ฉํด์ผํ๋์ง ๋ช
์๋์ด ์์ง ์์ ๋ฐ์ํ์ด์.
๋ก๊ทธ๋ฅผ ํ์ธ ํด๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ์ด ๋ ์ค ํ๋์ RequestMatcher
๋ฅผ ์ฌ์ฉํ์ฌ ๋ช
์ํด ๋ฌ๋ผ๊ณ ์น์ ํ๊ฒ ์๋ ค์ฃผ๊ณ ์์ด์.
This method cannot decide whether these patterns are Spring MVC patterns or not. If this endpoint is a Spring MVC endpoint, please use requestMatchers(MvcRequestMatcher); otherwise, please use requestMatchers(AntPathRequestMatcher).
Spring MVC ํจํด์ ์ฌ์ฉํ๊ณ ์๋ค๋ฉด MvcRequestMatcher
๋ฅผ ์ฌ์ฉํ๊ณ ๊ทธ๋ ์ง ์์ ๊ฒฝ์ฐ์๋ AntPathRequestMatcher
๋ฅผ ์ฌ์ฉํ๋ฉด ๋ผ์.
์๋ฌ๊ฐ ๋ฐ์๋ ์ฝ๋๋ฅผ ๋ณด๋ฉด ๋จ์ํ๊ฒ String ๋ฐฐ์ด๋ก RequestMatcher
๋ฅผ ๋ช
์ํด์ฃผ์ง ์๊ณ ์ฌ์ฉํ๊ณ ์์ด์.
์๋ฌ ๋ฐ์ ๋ถ๋ถ: .requestMatchers(WHITE_LIST).permitAll()
๋ฐ๋ผ์ ๋จ์ํ๊ฒ String ๋ฐฐ์ด์ ์ธ์๋ก ๋๊ฒจ์ฃผ๋ ๊ฒ์ด ์๋๋ผ, ์ฐ๋ฆฌ๋ Spring MVC ํจํด์ ์ฌ์ฉํ๊ณ ์๊ธฐ ๋๋ฌธ์ MvcRequestMatcher
๋ฅผ ์ธ์๋ก ๋๊ฒจ์ฃผ๋ฉด ๋ผ์.
@EnableMethodSecurity
@EnableWebSecurity
@Configuration
public class SecurityConfig {
private static final String[] WHITE_LIST = {
"/helloWorld",
};
@Bean
public MvcRequestMatcher.Builder mvcRequestMatcherBuilder(HandlerMappingIntrospector introspector) {
return new MvcRequestMatcher.Builder(introspector);
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http, MvcRequestMatcher.Builder mvc) throws Exception {
return http
.csrf(AbstractHttpConfigurer::disable)
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.headers(header -> header.frameOptions(HeadersConfigurer.FrameOptionsConfig::sameOrigin))
.authorizeHttpRequests(request -> request
.requestMatchers(PathRequest.toH2Console()).permitAll()
.requestMatchers(this.createMvcRequestMatcherForWhitelist(mvc)).permitAll()
.anyRequest().authenticated())
.build();
}
private MvcRequestMatcher[] createMvcRequestMatcherForWhitelist(MvcRequestMatcher.Builder mvc) {
return Stream.of(WHITE_LIST).map(mvc::pattern).toArray(MvcRequestMatcher[]::new);
}
}
HandlerMappingIntrospector
๋น์ ์ฃผ์
๋ฐ๊ณ , MvcRequestMatcher.Builder
์ธ์๋ก ๋๊ฒจ์ฃผ์ด ์ธ์คํด์ค๋ฅผ ์์ฑํ์ฌ ๋น์ผ๋ก ๋ฑ๋กํด์.createMvcRequestMatcherForWhitelist
๋ฉ์๋๋ฅผ ํตํด์ WHITE_LIST String[]
์ MvcRequestMathcer[]
๋ก ๋งคํํด์.requestMatchers
์ String[]
์ ์ ๋ฌํ๋ ๊ฒ์ด ์๋๋ผ createMvcRequestMatcherForWhitelist
๋ฉ์๋๋ฅผ ํตํด MvcRequestMathcer[]
์ ์ ๋ฌํด์ MvcRequestMatcher
์ฌ์ฉ์ ๋ช
์ํ์ฌ ์ด๋ฒ ์ด์๋ฅผ ํด๊ฒฐํ ์ ์์ด์.