Spring/문제 해결 (Troubleshooting)

context-path와 Security: 예상과 다른 경로 매칭 문제

가지코딩 2025. 7. 15. 11:26

Spring Boot 프로젝트에서 REST API 경로에 공통 prefix(/api)를 적용하기 위해 context-path 설정을 사용하는 경우가 많다.


하지만 이렇게 설정한 뒤, Spring Security에서 특정 경로를 허용하려 할 때, 의도한 대로 매칭되지 않고 403 혹은 401 에러가 발생하는 경우가 있다.


문제 상황

다음과 같이 context-path를 설정하고

server:
  servlet:
    context-path: /api

 

 

그리고 Security 설정에서는 아래와 같이 /api/login을 허용하도록 구성했다

http
    .authorizeHttpRequests(auth -> auth
        .requestMatchers("/api/login").permitAll()
        .anyRequest().authenticated()
    );

 

이 경우

클라이언트에서 /api/login으로 요청을 보내면 403 Forbidden 또는 401 Unauthorized 오류가 발생한다.


원인 분석

Spring Boot에서 server.servlet.context-path는 DispatcherServlet 레벨에서 모든 API 경로 앞에 /api prefix를 붙이게 된다.

 

예를 들어, 컨트롤러의 매핑이 다음과 같다면:

@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody LoginRequest request) {
    ...
}

 

실제 호출 가능한 경로는 /api/login이 된다.

 

하지만,

Spring Security는 context-path를 제거한 경로 기준으로 경로 매칭을 수행한다.

즉, requestMatchers("/api/login")는 실제 Security filter chain에서 존재하지 않는 경로로 간주된다.

 

Spring Security의 경로 매칭은 HttpServletRequest.getServletPath() 기준으로 이루어지며,

이 값은 context-path가 제외된 상태이다.

 

String servletPath = request.getServletPath(); // "/login"

해결 방법

Security 설정 시 context-path(prefix)를 포함하지 않고 경로를 지정

  • 실제 요청은 /api/login이지만,
  • Security 설정에서는 /login으로 매칭해야 한다.
http
    .authorizeHttpRequests(auth -> auth
        .requestMatchers("/login").permitAll() // "/api/login" 아님
        .anyRequest().authenticated()
    );