Spring Boot 프로젝트에서 전체 API는 Basic 인증을 사용하면서, 특정 API (/api/token/**)만 Bearer 토큰(JWT 등)을 사용하는 구조를 어떻게 구현할 수 있을까요?
Spring Security FilterChain을 분리하여 특정 API에만 토큰 인증을 적용하는 방법을 단계별로 설명 드리겠습니다.
1. 목표
- /api/token/** 경로: Bearer 토큰(JWT) 인증 적용
- 그 외 모든 경로: Basic Authentication 적용
2. 프로젝트 환경
- Java 11
- Spring Boot 3.x
- Spring Security 6.x 이상
- JWT 직접 구현 or 라이브러리(JJWT 등) 사용 가능
3. 디렉토리 구성 예시
src/
└─ main/
└─ java/
└─ com.example.demo/
├─ config/
│ └─ SecurityConfig.java
├─ filter/
│ └─ JwtAuthenticationFilter.java
├─ util/
│ └─ JwtTokenUtil.java
└─ controller/
└─ TokenApiController.java
4. Security 설정 – FilterChain 분리
Spring Security 6에서는 SecurityFilterChain을 여러 개 선언하고 @Order를 사용해 우선순위를 지정할 수 있습니다.
✅ SecurityConfig.java
@Configuration
@EnableWebSecurity
public class SecurityConfig {
// [1] JWT 인증이 적용될 FilterChain
@Bean
@Order(1)
public SecurityFilterChain jwtSecurityFilterChain(HttpSecurity http) throws Exception {
http
.securityMatcher("/api/token/**") // 이 경로에만 적용
.csrf(AbstractHttpConfigurer::disable)
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth
.anyRequest().authenticated()
)
.addFilterBefore(new JwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.exceptionHandling(ex -> ex
.authenticationEntryPoint(new JwtAuthenticationEntryPoint())
);
return http.build();
}
// [2] 나머지 경로는 Basic Auth 사용
@Bean
@Order(2)
public SecurityFilterChain basicAuthFilterChain(HttpSecurity http) throws Exception {
http
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(auth -> auth
.anyRequest().authenticated()
)
.httpBasic(Customizer.withDefaults());
return http.build();
}
}
securityMatcher()는 해당 FilterChain이 적용될 URL 경로를 지정합니다. 경로 외 요청은 다음 FilterChain으로 넘어갑니다.
5. JwtAuthenticationFilter 구현
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain)
throws ServletException, IOException {
String token = extractToken(request);
if (token != null && JwtTokenUtil.validateToken(token)) {
Authentication auth = JwtTokenUtil.getAuthentication(token);
SecurityContextHolder.getContext().setAuthentication(auth);
}
filterChain.doFilter(request, response);
}
private String extractToken(HttpServletRequest request) {
String bearer = request.getHeader("Authorization");
if (bearer != null && bearer.startsWith("Bearer ")) {
return bearer.substring(7);
}
return null;
}
}
6. JwtTokenUtil 유틸 (간단 예시)
public class JwtTokenUtil {
private static final String SECRET = "mySecretKey";
public static boolean validateToken(String token) {
// 실제로는 서명 검증, 만료 체크 등 포함
return true;
}
public static Authentication getAuthentication(String token) {
// 사용자 정보를 token에서 파싱 후 Authentication 객체 생성
UserDetails userDetails = User.withUsername("jwtUser").password("").roles("USER").build();
return new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
}
}
7. JWT 인증 실패 시 처리 (Optional)
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write("Unauthorized - Invalid or missing token");
}
}
8. 테스트용 컨트롤러 예시
@RestController
@RequestMapping("/api/token")
public class TokenApiController {
@GetMapping("/secure-data")
public ResponseEntity<String> secureData() {
return ResponseEntity.ok("JWT 인증에 성공한 사용자만 접근 가능!");
}
}
✅ 9. 결과 확인
요청 경로 | 인증 방식 |
/api/token/secure-data | Bearer Token (JWT) |
/api/other | Basic Auth |
마무리 정리
- Spring Security는 여러 개의 FilterChain을 선언하여 인증 방식을 분리할 수 있습니다.
- securityMatcher()를 사용하여 특정 경로에만 JWT 적용이 가능하며, 나머지는 기본 방식(Basic Auth)을 유지할 수 있습니다.
- 실제 JWT 검증은 JwtTokenUtil에 구현해두고, 서명 검증, 만료 체크 등을 추가해야 합니다.
'BackEND > Java' 카테고리의 다른 글
Vue 프론트엔드와 Spring Boot 백엔드 연동 구조 실전 가이드 (0) | 2025.05.13 |
---|---|
Spring Boot java11 환경에서 JWT 인증 + Refresh Token 적용하기 (2) | 2025.04.17 |
기존 Spring Boot JSP 프로젝트에서 Vue.js로 프론트엔드 교체하기 (1) | 2025.04.15 |
Spring Boot에서 REST API 설계 및 개발하기 (0) | 2025.04.06 |
실제 프로젝트에서 QueryDSL과 캐싱을 활용한 최적화 전략 (0) | 2025.04.05 |