배경
Spring Boot 3로 프로젝트를 개발하던 중 에러가 발생했다. 간단한 웹 페이지를 JSP로 구현하고 있었는데, 페이지를 열어보니 JSP 화면 대신 Whitelabel Error Page가 나타났다. 컨트롤러에서 main이라는 뷰 이름을 반환했고, 해당 JSP파일도 정확한 위치에 있었다. 그런데 왜 Spring Boot는 JSP를 찾지 못하고 Whitelabel Error Page를 표시하는걸까?
build.gradle
dependencies {
...
// JSP
implementation 'org.apache.tomcat.embed:tomcat-embed-jasper'
implementation 'org.glassfish.web:jakarta.servlet.jsp.jstl'
}
application.yml
spring:
mvc:
view:
prefix: /WEB-INF/views/
suffix: .jsp
SecurityConfig
@Configuration
public class SecurityConfig {
public static final String[] PUBLIC_URLS = {
"/", "/tools/**", "/login/**", "/error"
};
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(AbstractHttpConfigurer::disable)
.headers((headers) -> headers.frameOptions(FrameOptionsConfig::disable))
.authorizeHttpRequests((authorizeRequests) -> authorizeRequests
.requestMatchers(PUBLIC_URLS).permitAll()
.anyRequest().authenticated());
return http.build();
}
}
Contorller
@Slf4j
@RequiredArgsConstructor
@RestController
@RequestMapping("/tools")
public class AIToolController {
private final AiToolService aiToolService;
@GetMapping
public ModelAndView getMainPageInfo() {
ModelAndView mv = new ModelAndView();
mv.addObject("mainPageInfo", aiToolService.getMainPageInfo());
mv.setViewName("main");
return mv;
}
}
개발 환경
- Java 17
- Spring Boot 3.x
- Spring Security 6
- Gradle
- JSP
- H2
- IntelliJ
원인
If you are using Spring MVC to resolve view names, you will need to permit FORWARD requests. This is because when Spring MVC detects a mapping between view name and the actual views, it will perform a forward to the view. As we saw on the previous section, Spring Security 6.0 will apply authorization to FORWARD requests by default.
스프링 공식문서를 보면 Spring Security 5.8 버전까지는 기본적으로 요청당 한 번만 인증을 수행하기 때문에 이후의 FORWARD, INCLUDE 같은 Dispatcher Type 요청에 대해서는 인증을 수행하지 않았다. 6.0 버전부터는 모든 Dispatcher Type 요청에 대해서도 인증을 수행하도록 변경되었다. 이러한 변경으로 인해 View를 렌더링할 때, 특히 FORWARD 요청을 명시적으로 허용하도록 설정해야 한다.
JSP와 FORWARD가 무슨 상관일까?
FORWARD는 RequestDispatcher를 통해 서버 내부에서 다른 리소스로 요청을 전달하는 역할을 한다. DispatcherServlet은 ViewResolver를 통해 View 객체를 반환하는데, JSP를 사용할 경우 InternalResourceViewResolver가 사용한다. 이 과정에서 내부적으로 FORWARD 요청이 된다. Spring Security 6.0에서는 이 FORWARD 요청에 대해서도 인증을 수행하므로, 명시적인 허용 설정이 없으면 에러가 발생하게 된 것이다.
해결
Spring Security Config 설정에서 DispatcherType.FORWARD 요청을 허용하도록 추가했다.
@Configuration
public class SecurityConfig {
public static final String[] PUBLIC_URLS = {
"/", "/tools/**", "/login/**", "/error"
};
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(AbstractHttpConfigurer::disable)
.headers((headers) -> headers.frameOptions(FrameOptionsConfig::disable))
.authorizeHttpRequests((authorizeRequests) -> authorizeRequests
.requestMatchers(PUBLIC_URLS).permitAll()
.dispatcherTypeMatchers(DispatcherType.FORWARD).permitAll() // 추가
.anyRequest().authenticated());
return http.build();
}
}
참조
'Spring' 카테고리의 다른 글
Spring Boot - Repository 단위 테스트하기(JPA, Querydsl, Mybatis) (1) | 2024.12.31 |
---|---|
Spring Boot - Service 단위 테스트하기(JUnit5, Mockito, AssertJ) (1) | 2024.12.16 |
Spring Boot - Controller 단위 테스트하기(JUnit, MockMvc, Mockito) (5) | 2024.12.13 |
Spring Boot - JPA 스키마, 데이터 초기화하기 (SQL script -> ddl-auto) (1) | 2024.11.13 |
Spring Boot Test - Mockito로 Static Method Mock 만드는 방법 (2) | 2024.11.01 |