오늘은 수업 마지막 시간에 언급된 jwt에 대한 개념 정리를 해보려고 한다. 웹페이지라면 당연히 로그인이 빠질 수 없고 로그인을 위해서라면 보안이 빠질 수 없다. 내일 수업 진도를 나가기 전에 한 번 개념을 잡고 수업을 들으면 더욱 이해가 빠르겠지~


1. JWT 개념

  • JWT는 JSON을 안전하게 전송하기 위한 서명된 토큰으로
  • 주로 인증 후 서버가 사용자 정보를 토큰으로 만들어 클라이언트에 주고, 이후 요청마다 토큰을 보내 인증/인가에 사용한다
  • 토큰 자체에 클레임(claim, 사용자 정보나 유효기간) 등을 담는다
  • 서버는 토큰의 서명을 검증해 위·변조 여부를 판단한다

2. JWT 구성요소

JWT 형식 header.payload.signature (각각 base64url 인코딩)

  • Header: 알고리즘 (alg), 타입 (typ) 등. 예: { "alg":"RS256","typ":"JWT" }
  • Payload (Claims): sub(주체), exp(만료), iat(발행시간), roles 등 사용자 정보
  • Signature: Header+Payload를 비밀키/개인키로 서명한 값 (변조 방지)

대칭키(HS256) vs 비대칭키(RS256)

HS256 (대칭): 하나의 비밀키로 서명/검증. 구현 간단. 키가 유출 되면 위험.
RS256 (비대칭): private 키로 서명, public 키로 검증. 마이크로서비스나 외부 검증이 필요할 때 안전.


3. Spring에서 JWT를 다루는 방법(개요)

  • Spring Security는 JWT를 직접 만드는 유틸을 제공하지는 않았지만, Nimbus JOSE + JWT를 지원하는 컴포넌트를 제공한다
  • Spring Security 5.5+ 에서는 JwtEncoder, JwtDecoder (Nimbus 기반)를 통해 JWT 생성/검증을 편하게 구성할 수 있다
    • JwtEncoder → 서버가 토큰을 “발급(encode)”
    • JwtDecoder → 클라이언트로부터 받은 토큰을 “검증(decode)”

핵심 흐름

  1. 로그인(인증) 성공 → JwtEncoder를 사용해 JWT 발급

  2. 클라이언트는 Authorization: Bearer <token>으로 요청

  3. Spring Security가 JwtDecoder로 서명 확인 및 claim 추출 → 인증 컨텍스트 세팅 → Controller 접근 가능


4. 실습 예제 코드

1) 프로젝트 준비(의존성 추가)

  • spring-security-oauth2-jose는 Nimbus를 통해 JWT를 생성/검증하는 데 필요한 클래스(JwtEncoder, JwtDecoder, NimbusJwtEncoder 등)를 제공

2) RSA 키 생성 예시 (서버 시작 시 자동 생성)

import항목

구현 코드

  • Spring Boot + Spring Security + Nimbus
  • KeyPair는 public key(검증용) + private key(서명용) 한 쌍
  • 실무에서는 이 키를 하드코딩하지 않고, 외부 파일/KeyStore/Vault 등에 안전하게 보관해야 함

3) Spring Security 설정 (JwtEncoder / JwtDecoder 빈 등록)

import항목

구현 코드

  • JwtEncoder로 토큰 생성, JwtDecoder로 검증
  • RSAKey.Builder : RSA 공개키·개인키를 담은 Nimbus용 RSAKey 객체 생성
  • JwtEncoder는 private key로 서명된 토큰 생성 담당
  • JwtDecoder는 public key로 검증 담당
  • 둘 다 Spring Bean으로 등록되어 Controller나 Security Filter에서 자동 주입됨

4) 로그인 API (JwtEncoder 사용 토큰 발급)

로그인 성공 시 JwtEncoder로 토큰을 만들어 응답

  • 위 예제는 username="user", password="pass"일 때 토큰 발급하지만, 실제 서비스에선 UserDetailsService로 사용자 조회 및 패스워드 검증 필요!
  • JwtClaimsSet.builder()는 JWT 본문의 데이터를 담는 빌더 객체
  • encode()는 Header + Payload를 서명 후 토큰 문자열로 반환
  • 반환 타입 Map<String,String>{ "accessToken": "<토큰문자열>" }

5) Jwt 검증 및 Spring Security 연동

  • SecurityFilterChain은 Spring Security 필터 구성을 정의
  • .oauth2ResourceServer().jwt()는 JWT를 Authorization 헤더에서 자동 추출 → 검증 → Authentication 세팅을 의미
  • .antMatchers("/auth/**").permitAll() : 로그인 등은 인증 없이 접근 가능

보호된 API 예시

  • 클라이언트는 Authorization: Bearer <accessToken> 헤더를 붙여 /profile에 요청하면 인증된 정보가 반환됨
  • @AuthenticationPrincipal은 현재 인증된 사용자의 principal 객체를 주입
  • JWT 인증 시 이 값이 Jwt 타입이 됨
  • jwt.getSubject() → sub claim 반환 (보통 username)
  • jwt.getClaims() → 모든 claim(Map<String,Object>) 반환

6) 토큰 요청 테스트

총 정리

항목핵심 요약
JwtEncoder / JwtDecoderNimbus 기반 JWT 발급·검증 클래스
JwtClaimsSet토큰에 담길 데이터(issuer, subject, exp 등)
JwsHeader서명 알고리즘 및 header 정보
Jwt실제 검증된 토큰 객체, claims 포함
@AuthenticationPrincipal JwtSecurity가 자동으로 주입하는 인증 정보
HttpSecurity.oauth2ResourceServer().jwt()JWT 인증 필터 활성화
KeyPairRSA 공개키/개인키 쌍

🔹오늘의 나는 무엇을 잘했는지
작은 것이라도 내가 오늘 해낸 일, 스스로 칭찬하고 싶은 부분 등
예) “처음엔 막막했지만, 복습 후에 기능 구현을 완성했다.”

🔹어떤 문제를 겪었고, 어떻게 해결했는지
수업 막바지에 jwt관련 설명을 들었다. 앞서 수업에서 다룬 spring 자체의 로그인 시스템도 익숙하지는 않았는데 뒷쪽 부분은 django를 사용할 때 공부했던 내용이었지만 사람은 망각의 동물이라는 것... 그래서 이렇게 velog에 한 번 더 정리하면서 기억을 되짚어봤다.

🔹오늘 배운 것
오늘은 spring의 로그인 및 보안 시스템에 대해서 학습할 수 있었고 얼마나 정교하게 만들었고 개발자가 어떻게 커스텀하여 사용할 수 있는지 학습했다.

🔹좋았던 점 & 아쉬웠던 점
늘 그렇듯 가장 처음에는 기본 코드부터 시작해서 한 가지 두 가지씩 업그레이드하여 프로젝트를 새롭게 구성해보면서 단계별로 학습을 시켜주신다. 이 부분이 언제나 항상 너무 감사한 것 같다😊

🔹나만의 팁 or 복습 방법
오늘처럼 수업 진도가 나가기 전에 한 번 관련 개념에 대해서 공부를 하는 것도 학습에 도움이 많이 되는 것 같다. 아예 처음 수업에서 공부를 하는 것 보다 다음 수업시간의 키워드가 잡히면 해당 개념에 대해서 다양한 방면으로 얕게라도 공부하고 수업을 듣는 것이 학습효과를 강화해주는 것 같다.

0개의 댓글