5단계
5단계 — 인증·JWT
30 분
5단계 — 인증·JWT
REST API 는 stateless — 서버에 세션을 저장하지 않아요. 매 요청마다 증명서 를 가지고 와야 합니다. 그 증명서가 JWT (JSON Web Token).
JWT 한 장 보기
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0In0.signature
세 부분을 점(.) 으로 구분:
- Header — 알고리즘 (HS256, RS256 등)
- Payload — 사용자 ID·만료 시간 등 (서명되어 변조 불가)
- Signature — 서버 비밀키로 만든 해시
서버는 새로 발급한 토큰을 클라이언트에 주고, 클라이언트는 매 요청마다 Authorization: Bearer <token> 헤더로 가져옵니다.
발급 플로우
1. POST /api/auth/login (email, password)
↓
2. 서비스가 비밀번호 검증 (BCrypt)
↓
3. JWT 발급 (sub=userId, exp=now+1h)
↓
4. 응답: { accessToken: "eyJ..." }
Spring Security 한 줄 설정
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.csrf(csrf -> csrf.disable()) // REST API 는 보통 disable
.sessionManagement(s -> s.sessionCreationPolicy(STATELESS))
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/auth/**").permitAll()
.anyRequest().authenticated()
)
.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)
.build();
}
}
STATELESS + JWT 필터 가 핵심.
토큰 검증 필터
@Component
@RequiredArgsConstructor
public class JwtFilter extends OncePerRequestFilter {
private final JwtService jwtService;
@Override
protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws ServletException, IOException {
String header = req.getHeader("Authorization");
if (header != null && header.startsWith("Bearer ")) {
String token = header.substring(7);
Long userId = jwtService.parseUserId(token);
// SecurityContext 에 인증 정보 등록
// …
}
chain.doFilter(req, res);
}
}
직접 해 보기
POST /api/auth/login 엔드포인트를 만들고 curl -X POST http://localhost:8080/api/auth/login -d '{"email":"...","password":"..."}' 로 토큰을 받아 보세요. 그다음 curl http://localhost:8080/api/posts -H "Authorization: Bearer <token>" 로 보호된 API 를 호출.
더 깊이
다음 단계
6단계에서는 이 모든 코드가 안전한지 자동으로 확인하는 테스트를 배워요.