Spring Boot로 구현하는 REST API 보안 전략 (JWT & OAuth2)
“내 API, 외부 침입으로부터 어떻게 지킬 수 있을까?” REST API를 설계할 때 보안은 선택이 아닌 필수입니다. 오늘은 Spring Boot 기반에서 JWT와 OAuth2를 활용한 보안 전략을 알아봅니다.
안녕하세요, 개발자 여러분! ICT리더 리치입니다. 클라이언트와 서버 간 데이터가 JSON 형태로 주고받는 REST API는 매우 편리하지만, 그만큼 보안 위협에도 쉽게 노출될 수 있습니다. 특히 민감한 사용자 데이터나 인증 시스템을 다룰 경우, 보안 전략은 프로젝트의 핵심이 되죠.
오늘 포스팅에서는 Spring Boot 환경에서 JWT(Json Web Token)와 OAuth2를 활용해 안전하게 API를 보호하는 방법을 단계별로 설명드릴게요. 실무에 바로 적용 가능한 예시와 설정도 함께 제공하니 끝까지 집중해주세요!
📌 바로가기 목차
| JWT 인증 보안 구현을 상징하는 여성 개발자 프로필 이미지 |
1. REST API에서 보안이 중요한 이유
REST API는 클라이언트와 서버 간의 통신을 단순화하고 유연하게 해주지만, HTTP 기반이기 때문에 중간자 공격, 세션 탈취, API 남용 등의 보안 위협에 취약할 수 있습니다. 특히 로그인, 회원정보, 결제와 같은 민감한 데이터를 다루는 경우 보안은 선택이 아니라 필수입니다. 사용자 인증, 접근 권한 제어, 데이터 암호화 같은 보안 조치를 적용해야 하는 이유가 여기에 있습니다.
2. JWT(Json Web Token) 개념과 구조
JWT는 사용자 인증 정보를 JSON 형식으로 안전하게 전송하는 방식으로, 자체적으로 정보를 포함하고 있어 세션 저장소가 필요 없는 무상태 인증(stateless)을 구현할 수 있습니다. 다음은 JWT의 기본 구조입니다.
| 구성 요소 | 설명 |
|---|---|
| Header | 토큰 타입(JWT)과 서명 알고리즘(HMAC, SHA256 등)을 명시 |
| Payload | 사용자 정보, 권한, 토큰 만료 시간 등 클레임 정보 포함 |
| Signature | 비밀키와 Header, Payload를 해싱하여 위변조 방지 |
보안 설계는 시스템의 기반 구조를 안전하게 유지하기 위한 핵심 과정입니다. 다음 예시는 JWT 토큰을 생성하고 유효성을 검증하는 과정을 담고 있습니다.
// JWT 생성 및 검증 예제
import io.jsonwebtoken.*;
import java.util.Date;
public class JwtProvider {
private static final String SECRET_KEY = "mySecretKey123456";
private static final long EXPIRATION_TIME = 86400000L; // 1일
public static String generateToken(String username) {
return Jwts.builder()
.setSubject(username)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(SignatureAlgorithm.HS256, SECRET_KEY)
.compact();
}
public static String validateToken(String token) {
try {
Claims claims = Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token)
.getBody();
return claims.getSubject();
} catch (Exception e) {
return null;
}
}
public static void main(String[] args) {
String token = generateToken("adminUser");
System.out.println("JWT: " + token);
System.out.println("검증 결과: " + validateToken(token));
}
}
3. Spring Boot에서 JWT 인증 구현하기
Spring Boot에서 JWT 인증을 구현하려면, 다음과 같은 구성 요소가 필요합니다. 핵심은 사용자 인증 정보를 기반으로 JWT 토큰을 생성하고, 이후 요청마다 이를 검증하는 흐름을 구성하는 것입니다.
- UserDetailsService로 사용자 정보 로딩
- JwtUtil 클래스로 토큰 생성 및 파싱
- JwtAuthenticationFilter로 요청 시 토큰 검증
- Spring Security 설정으로 인증 처리 흐름 구성
Spring Boot에서 JWT 인증을 적용하기 위해서는 필터를 통해 요청마다 토큰을 검증하고, SecurityContext에 인증 정보를 설정해야 합니다. 아래는 JWT 필터의 기본 구현 예시입니다.
// JWT 인증 필터 구현 예시
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtProvider jwtProvider;
public JwtAuthenticationFilter(JwtProvider jwtProvider) {
this.jwtProvider = jwtProvider;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
String token = jwtProvider.resolveToken(request);
if (token != null && jwtProvider.validateToken(token)) {
String username = jwtProvider.getUsername(token);
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(username, null, null);
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
filterChain.doFilter(request, response);
}
}
![]() |
| Spring Boot 보안 전략 – JWT와 OAuth2 인증 흐름을 설명하는 인포그래픽 |
4. OAuth2 인증 방식의 이해
OAuth2는 외부 서비스(예: 구글, 네이버, 깃허브 등)의 인증 시스템을 활용하여 사용자를 인증하는 표준 프로토콜입니다. 사용자 비밀번호를 직접 저장하지 않고도 인증이 가능하여 보안성과 사용자 편의성이 뛰어납니다. 아래는 OAuth2의 기본 흐름입니다.
| 단계 | 설명 |
|---|---|
| 1. 사용자 인증 요청 | 클라이언트가 인증 서버에 로그인 요청 |
| 2. 인증 코드 발급 | 사용자 인증 후 인증 코드 반환 |
| 3. 토큰 요청 | 클라이언트가 인증 코드를 사용해 액세스 토큰 요청 |
| 4. 토큰 발급 | 인증 서버가 액세스 토큰 발급 |
OAuth2는 인증 서버를 통해 Access Token을 발급받고 이를 통해 리소스 서버에 접근하는 방식입니다. Spring Security에서 이를 구현하려면 Client Registration 설정과 사용자 정보를 처리하는 서비스가 필요합니다.
// OAuth2UserService 구현 예시
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service;
import java.util.Map;
@Service
public class CustomOAuth2UserService extends DefaultOAuth2UserService {
@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) {
OAuth2User oAuth2User = super.loadUser(userRequest);
Map attributes = oAuth2User.getAttributes();
String email = (String) attributes.get("email");
String name = (String) attributes.get("name");
String provider = userRequest.getClientRegistration().getRegistrationId();
// 사용자 정보 저장 또는 업데이트 처리
saveOrUpdateUser(email, name, provider);
return oAuth2User;
}
private void saveOrUpdateUser(String email, String name, String provider) {
System.out.println("OAuth2 사용자 등록: " + email + ", 이름: " + name + ", 제공자: " + provider);
// 실제 DB 처리 로직 작성 가능
}
}
5. Spring Security 기반 OAuth2 연동 방법
Spring Boot에서 OAuth2 인증을 구현할 때는 spring-boot-starter-oauth2-client를 활용해 손쉽게 외부 인증 서버와 연동할 수 있습니다. 핵심은 client-id, client-secret, redirect-uri 등의 설정입니다.
- application.yml에 클라이언트 정보 등록
- OAuth2LoginSuccessHandler를 통해 사용자 정보 가공
- CustomOAuth2UserService로 사용자 정보 추출
- Spring Security 설정 클래스에 OAuth2 설정 통합
OAuth2를 Spring Boot에 연동하려면 application.yml 설정과 함께 WebSecurityConfigurerAdapter를 통한 시큐리티 설정이 필요합니다. 다음 예시는 클라이언트 등록과 경로별 인증 적용을 설정한 구성입니다.
// Spring Security OAuth2 설정 예시
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.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authz -> authz
.requestMatchers("/", "/login", "/css/**", "/js/**").permitAll()
.anyRequest().authenticated()
)
.oauth2Login(oauth2 -> oauth2
.loginPage("/login")
.defaultSuccessUrl("/dashboard")
.failureUrl("/login?error=true")
)
.logout(logout -> logout
.logoutSuccessUrl("/")
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID")
);
return http.build();
}
}
6. 보안을 위한 실전 팁 & 주의사항
보안을 실제 서비스에 적용할 때는 다음의 팁을 반드시 고려하세요. 단순히 토큰을 발급한다고 해서 완전한 보안이 되는 것은 아닙니다. 구성과 운영 모두에서 철저한 방어가 필요합니다.
- HTTPS를 반드시 사용하여 네트워크 구간 암호화 적용
- JWT의 만료 시간 설정 및 Refresh Token 활용
- OAuth2의 redirect URI는 화이트리스트 방식으로 제한
- CORS 정책 및 CSRF 설정 반드시 점검
OAuth2를 통해 인증된 사용자 정보를 기반으로 JWT를 발급하면, 인증과 인가를 분리한 아키텍처를 구성할 수 있습니다. 다음은 OAuth2 로그인 성공 후 JWT를 발급하는 필터 예제입니다.
// OAuth2 로그인 후 JWT 발급 필터 예시
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.io.PrintWriter;
@Component
public class OAuth2SuccessHandler implements AuthenticationSuccessHandler {
private final JwtProvider jwtProvider;
public OAuth2SuccessHandler(JwtProvider jwtProvider) {
this.jwtProvider = jwtProvider;
}
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
String username = authentication.getName();
String token = jwtProvider.generateToken(username);
response.setContentType("application/json;charset=UTF-8");
PrintWriter writer = response.getWriter();
writer.println("{\"token\":\"" + token + "\"}");
writer.flush();
}
}
![]() |
| Spring Boot에서 OAuth2 기반 REST API 보안을 설명하는 고급 인포그래픽 |
7. 자주 묻는 질문 (FAQ)
JWT는 서버에 세션 상태를 저장할 필요가 없는 무상태(stateless) 구조이므로, 수평적 확장이 필요한 마이크로서비스 환경에 더 적합합니다. 또한 토큰에 정보를 포함하므로 인증 속도가 빠르며, 클라이언트 독립적인 인증 처리에 유리합니다.
HTTPS 적용은 기본이며, 짧은 만료 시간 설정과 함께 Refresh Token 전략을 사용해야 합니다. 토큰 재사용 감지를 위한 Redis 블랙리스트 캐시 전략도 병행하면 좋습니다.
OAuth2는 외부 인증 제공자와의 신뢰 기반이므로, 리디렉션 URL 검증과 사용자 정보 위조 방지를 위한 state 파라미터 적용이 필수입니다. 또한 각 인증 제공자의 스펙 차이를 고려한 처리 로직이 필요합니다.
OAuth2는 사용자 인증 수단, JWT는 인증 이후의 인가 및 API 보안 수단으로 활용됩니다. 인증과 인가의 책임 분리를 통해 보안성과 유연성을 동시에 확보할 수 있습니다.
보안 관점에서는 HttpOnly 쿠키를 사용하는 것이 XSS에 안전합니다. 로컬스토리지는 XSS에 노출될 가능성이 있으므로 민감한 정보 저장에는 적합하지 않습니다.
8. 마무리 요약
✅ 보안은 선택이 아닌 필수, API 보호의 첫걸음
REST API는 편리하지만, 외부에 노출되기 쉬운 구조이기 때문에 강력한 보안 전략이 필요합니다.
JWT는 무상태 인증 시스템으로, OAuth2는 외부 인증 연동의 표준으로 각각의 역할이 명확하죠.
오늘 포스팅에서는 Spring Boot 환경에서 두 보안 방식을 어떻게 실전에서 구현할 수 있는지 구체적으로 알아보았습니다.
지금 이 순간에도 수많은 공격이 이루어지고 있는 만큼,
탄탄한 인증 시스템과 실전 보안 수칙으로 여러분의 서비스를 안전하게 지켜내세요!


댓글
댓글 쓰기