generateToken( )

로그인 인증이 완료된 authentication 객체로 accessToken을 생성하는 함수

public String generateToken(Authentication authentication) {
	String email = authentication.getName();
	
	return Jwts.builder()
		.setSubject(email)
		.claim("role", authentication.getAuthorities().iterator().next().getAuthority())
		.setIssuedAt(new Date())
		.setExpiration((new Date(System.currentTimeMillis() + jwtProperties.getExpiration().getAccess()))
		.signWith(getSigningKey(), SignatureAlgorithm.HS256)
		.compact();

여기서 SecretKey만 HS256 알고리즘에 사용되는 것처럼 보이고 다른것들은 문자열인데 어떻게 compact()를 실행했을 때 문자열을 반환하는 건지 궁금했다.

// 서명 키 생성
    // 바이트 배열로 변환해서 HS256 알고리즘용 Secret Key 생성
    private Key getSigningKey() {
        return Keys.hmacShaKeyFor(jwtProperties.getSecretKey().getBytes());
    }

여기서 Key 형태로 반환된 SecretKey를 받아서

.signWith(getSigningKey(), SignatureAlgorithm.HS256)를 실행하면, Header와 Payload를 이용해 SecretKey로 HMAC-SHA256 서명을 생성한다.

Header와 Payload를 Base64Url 인코딩한 뒤 Base64Url(Header) + "." + Base64Url(Payload) 형태의 문자열을 만든다.

eyJhbGciOiJIUzI1NiJ9. eyJzdWIiOiJ0ZXN0QHRlc3QuY29tIiwicm9sZSI6IlJPTEVfVVNFUiJ9

이러한 문자열을 만들어서

HMACSHA256(
    위 문자열,
    SecretKey
)

를 계산하고

aBcDeFgHiJkLmNo… 같은 서명 값(Signature)이 나온다.

그리고 Signature를 Base64Url로 인코딩

최종으로 compact()를 실행하면, 인코딩된 Header + Payload + Signature 세가지가 합쳐져 JWT 토큰이 생성된다. ex)Header.Payload.Signature(xxxxx.yyyyy.zzzzz) 구조

eyJhbGciOiJIUzI1NiJ9. eyJzdWIiOiJ0ZXN0QHRlc3QuY29tIiwicm9sZSI6IlJPTEVfVVNFUiJ9. aBcDeFgHiJkLmNo...

이런식으로 JWT가 생성된다.

결과:

Header

{
  "alg": "HS256",
}

Payload

{
  "sub": "[email protected]",
  "role": "ROLE_USER",
  "iat": 1710000000,
  "exp": 1710003600
}

Signature

HMACSHA256(
    Base64Url(Header) + "." + Base64Url(Payload),
    SecretKey
)

검증할 때는

Jwts.parserBuilder()
    .setSigningKey(getSigningKey())
    .build()
    .parseClaimsJws(token);

를 해서 토큰의 Signature와 내가 가진 SecretKey로 다시 계산한 Signature를 비교해 진짜 토큰인지 판단한다.

Base64: 바이너리 데이터나 특수 문자가 포함된 데이터를 안전하게 문자열로 표현하는 방식

JWT는 URL이나 HTTP Header에 들어가기 때문에 일반 Base64의 +, /를 -로 바꾸고 =을 제거한 것이 Base64URL이다.

Base64URL은 누구나 디코딩 가능한 암화화가 아니기때문에 민감정보를 넣으면 안됨.

Base64Url은 암호화가 아닌 인코딩 방식이다.

누구나 디코딩하여 Payload 내용을 확인할 수 있으므로 비밀번호, 주민등록번호, 카드번호 등 민감한 정보는 JWT Payload에 저장하면 안 된다.

요약 정리

<aside> 📝

  1. Authentication 객체에서 이메일과 권한을 꺼낸다.

  2. JWT Payload를 생성한다.

    Payload { "sub": "[email protected]", "role": "ROLE_USER", "iat": 1710000000, "exp": 1710003600 }

  3. getSigningKey()에서 SecretKey 문자열을 HS256 알고리즘에서 사용할 수 있는 Key 객체로 변환한다.

  4. signWith(getSigningKey(), SignatureAlgorithm.HS256)를 실행하면

    HMACSHA256( Base64Url(Header) + "." + Base64Url(Payload), SecretKey )

    를 통해 Signature를 생성한다.

    생성된 Signature은 인코딩됨.

  5. compact()는 Header.Payload.Signature

    형태의 JWT 문자열로 생성한다.

  6. SecretKey는 JWT 내부에 저장되지 않는다.

  7. 검증 시

    Jwts.parserBuilder() .setSigningKey(getSigningKey()) .build() .parseClaimsJws(token);

    를 실행하면,

    토큰에 포함된 Signature와 서버가 가진 SecretKey로 다시 계산한 Signature를 비교한다.

  8. 두 Signature가 일치하면 서버가 발급한 정상 토큰으로 판단하고, 일치하지 않으면 위변조된 토큰으로 판단한다. </aside>

최종 수정일: 2026-06-18


@하진 Hajin’s Portfolio


📧 contact [email protected]