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> 📝
Authentication 객체에서 이메일과 권한을 꺼낸다.
JWT Payload를 생성한다.
Payload { "sub": "[email protected]", "role": "ROLE_USER", "iat": 1710000000, "exp": 1710003600 }
getSigningKey()에서 SecretKey 문자열을 HS256 알고리즘에서 사용할 수 있는 Key 객체로 변환한다.
signWith(getSigningKey(), SignatureAlgorithm.HS256)를 실행하면
HMACSHA256( Base64Url(Header) + "." + Base64Url(Payload), SecretKey )
를 통해 Signature를 생성한다.
생성된 Signature은 인코딩됨.
compact()는 Header.Payload.Signature
형태의 JWT 문자열로 생성한다.
SecretKey는 JWT 내부에 저장되지 않는다.
검증 시
Jwts.parserBuilder() .setSigningKey(getSigningKey()) .build() .parseClaimsJws(token);
를 실행하면,
토큰에 포함된 Signature와 서버가 가진 SecretKey로 다시 계산한 Signature를 비교한다.
두 Signature가 일치하면 서버가 발급한 정상 토큰으로 판단하고, 일치하지 않으면 위변조된 토큰으로 판단한다. </aside>
최종 수정일: 2026-06-18
@하진 Hajin’s Portfolio
📧 contact [email protected]