Java/인증 및 인가, IAM

[Java] Spring Boot Security 이해하기 -3: JWT(JSON Web Token) 이해하기

adjh54 2022. 12. 21. 23:00
728x170
해당 글에서는 Spring Security의 인증을 위한 ‘JWT: JSON Web Token’를 이해하고 적용하기 위해 우선 이해를 목적으로 작성한 글입니다. 추후 적용을 위한 환경 설정 방법에 대해서 공유합니다.

 

 

 

 

💡 [참고] 해당 글에서는 이전에 작성한 Spring Boot Security 기반으로 추가 구성합니다.
 

[Java] Spring Boot Security 이해하기 -2 : 5.7.x 버전 구현하기

해당 글에서는 이전에 Spring Security 5.7.x 버전에 대해 이해한 내용을 기반으로 실제 구현하는 방법에 대해서 공유합니다. [참고] Spring Boot Security를 적용하기 이전의 이해하기 위한 글을 참고하시

adjh54.tistory.com

 

 

1) JWT(JSON Web Token) 


💡 JWT(JSON Web Token)

- 모바일이나 웹의 ‘사용자 인증’을 위해 '인증'에 필요한 정보를 토큰에 담아서 암호화를 시켜 사용하는 인터넷 표준 인증 방식을 의미합니다. 가볍고 자가 수용적인(self-contained) 방식으로 정보를 안전성 있게 전달해줍니다.

 

[ 더 알아보기 ] 

💡 자가수용적인 (self-contained) 방식

- JWT는 필요한 모든 정보를 자체적으로 지니고 있다는 장점이 있습니다. 이는 JWT에서 발급된 토큰은 기본 정보, 전달할 정보, 검증에 대한 모든 Signature도 포함하고 있으며, 웹 서버 환경에서는 HTTP 헤더에 포함을 시키거나 URL의 파라미터로 전달이 가능합니다.

 

💡 JWT 이용한 응용 방안 - 프로세스로 이해하는 JWT
  • 클라이언트에서 서버로 최초 호출이 발생하는 경우 Header 내에 토큰의 존재 여부를 체크합니다.
    • 토큰이 존재하지 않을 경우 ‘로그인’ 페이지로 리다이렉트 시켜 로그인 후 토큰을 발급 받는다.
    • 토큰이 존재하는 경우 토큰의 만료시간을 확인한다.
      • 토큰이 만료되었을 경우 재 발급을 위해 ‘로그인’ 페이지로 리다이렉트 시켜 로그인 후 토큰을 재 발급 받는다.
      • 토큰이 유효한 경우 토큰 내의 정보를 확인하여 가져온다.
  • 클라이언트에서 서버로 이후 호출이 발생하는 경우 토큰의 만료시간을 확인한다.
    • 토큰이 만료된 경우 ‘로그인’ 페이지로 리다이렉트 시켜 로그인 후 토큰을 발급 받는다.
    • 토큰이 만료되지 않은 경우 정상적인 프로세스를 진행한다.

 

 

2) JWT 구조 이해하기 


 

1. JWT 구조


💡 JWT 구조

- JWT는 점(.)으로 구분된 세 부분으로 구성되어 있습니다.

header . payload . signature

이는 서버에서 각각 header, payload, signature를 구성한 값을 인코딩하여서 사용이 됩니다.
이에 대해서 각각을 확인 해 봅니다.

 

[참고] 공식 사이트
 

JWT.IO

JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties.

jwt.io

 

 

 

2. JWT의 헤더(header)


💡헤더(header)

- 첫번째, 헤더(header)의 구성은 토큰의 타입서명(Signature)에서 사용할 알고리즘으로 구성되어 있습니다.

 

[참고] JWT의 헤더 구조 예시 
{
  "typ": "JWT",
  "alg": "HS256"
}

 

요소 요소의 key 값 요소의 value 값
토큰의 타입 typ JWT
토큰의 서명에서 사용할 암호화 알고리즘 종류 alg HMAC, SHA256, RSA, HS256, RS256

 

💡 구성한 헤더(Header)의 JSON은 ‘Base64Url’로 인코딩 되어서 JSON 웹 토큰의 첫 번째 요소로 사용이 됩니다.

 

[참고] 헤더 생성 예시
/**
 * 헤더 값을 생성해주는 메서드
 *
 * @return HashMap<String, Object> 헤더 값
 */
private static Map<String, Object> createHeader() {
    Map<String, Object> header = new HashMap<>();
    header.put("typ", "JWT");
    header.put("alg", "HS256");
    header.put("regDate", System.currentTimeMillis());
    return header;
}

 

 

 

3. JWT의 페이로드(payload)


💡 페이로드(payload)

- 두 번째, 페이로드(payload)의 구성은 인증을 위해 사용할 실제 정보들(클레임)으로 구성이 되어 있습니다.

- 이 클레임의 종류는 등록 클레임(Registered claims), 공개 클레임(Public claims), 비공개 클레임(Private claims)의 세 가지로 구성되어 있습니다.

 

 

[참고] JWT의 페이로드 구조 예시 
{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}

 

[ 더 알아보기 ]

💡 클레임(Claims)

- 페이로드 구성에 담을 key와 value의 한쌍으로 이루어 진 형태를 클레임이라고 합니다.
- 위에 예시로는 "sub": "1234567890"가 하나의 클레임입니다.

 

3.1. 등록 클레임(Registered Claim) 

💡  필수로 사용되는 정보는 아니지만 JWT의 클레임에서 정의된 키 값을 기준으로 선택하여 사용하는 클레임입니다.
요소 키 값 키 값 설명 예시
"iss" : Issuer Claim 토큰 발급자 정보 {"iss" : "sendApiToken"}
"sub": Subject Claim 토큰 제목 정보 {"sub": "customToken"}
"aud": Audience Claim 토큰 대상자 정보 {"aud": "reciveClientToken"}
"exp": Expiration Time Claim 토큰 만료시간 정보 {"exp": "1671629718"}
"nbf": Not Before Claim 토큰 활성 날짜 {"nbf": "1671629999"}
"lat": Issued At Claim 토큰 발급 시간 {"lat": "1671629999"}
"jti": JWT ID Claim  토큰 식별자 {"jti": "jwtidis"}

 

 

 

3.2. 공개 클레임(Public Claim)

💡 사용자가 정의하는 클래임으로 공개용 정보를 위해 사용되며 충돌 방지를 위해 URI 포맷을 이용하는 클레임입니다.
{
  "https://adjh54.tistory.com/92": true,
}

 

 

 

3.3. 비공개 클레임(Private Claim) 

💡 클라이언트와 서버간의 실제 데이터를 전송하려는 것에 대한 정보로 선택하여 사용하는 클레임입니다.
{
  "userId": "adjh54",
  "userEmail": "adjh54@gmail.com",
}

 

💡 구성한 페이로드(Payload)의 JSON은 ‘Base64Url’로 인코딩 되어서 JSON 웹 토큰의 두 번째 요소로 사용이 됩니다.

 

[참고] 페이로드 생성 예시
/**
 * 사용자 정보를 기반으로 클래임을 생성해주는 메서드
 *
 * @param userDto 사용자 정보
 * @return Map<String, Object>
 */
private static Map<String, Object> createClaims(UserDto userDto) {
    // 공개 클레임에 사용자의 이름과 이메일을 설정하여 정보를 조회할 수 있다.
    Map<String, Object> claims = new HashMap<>();

    log.info("userId :" + userDto.getUserId());
    log.info("userNm :" + userDto.getUserNm());

    claims.put("userId", userDto.getUserId());
    claims.put("userNm", userDto.getUserNm());
    return claims;
}

 

 

 

4. JWT의 서명(signature)


💡 백엔드에서 발급되는 서명(Signature)은 인코딩된 헤더(Header)와 인코딩된 페이로드(Payload), 비밀 키(Secret Key)와 헤더에 지정된 서명에 사용 할 알고리즘을 기반으로 발급이 됩니다.

💡 서명(Signature) = 인코딩 헤더 + 인코딩 비밀키 + 비밀 키 + 헤더의 알고리즘

 

HMAC SHA256 알고리즘을 기반으로 서명은 아래와 같은 방식으로 구성이 됩니다
HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

 

💡 구성한 서명(Signature)은 JSON 웹 토큰의 마지막 세 번째로 요소로 사용이 됩니다.

 

[참고] 구성한 서명을 아래의 디버깅을 통해 디코딩을 수행 할 수 있습니다.
 

JWT.IO

JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties.

jwt.io

 

[참고] 서명 생성 예시
private static String jwtSecretKey = "jwtSecretKey";

/**
 * JWT 서명(Signature) 발급을 해주는 메서드
 *
 * @return Key
 */
private static Key createSignature() {
    byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(jwtSecretKey);
    return new SecretKeySpec(apiKeySecretBytes, SignatureAlgorithm.HS256.getJcaName());
}

 

💡 다음 JWT에 대한 구현 내용은 아래의 글에서 이어집니다.
 

[Java] Spring Boot Security 이해하기 -4: JWT 환경 설정 및 구성 하기

해당 글에서는 Spring Boot Security내에 ‘인증’ 방식을 JWT를 이용하여서 사용자의 인증을 구성하는 환경 설정방법에 대해서 이해하기 위한 글입니다. [참고] 해당 글은 이전에 작성한 'JWT 이론'에

adjh54.tistory.com

 

[참고] 사용자 정보를 전달하기 전에 Spring Security의 구성을 이해하시면 이후 환경설정에 도움이 됩니다.
 

[Java] Spring Boot Security 이해하기 -1 : 5.7.x 버전 구조 및 파일 이해

해당 글에서는 Spring Boot 기반의 Spring Security Framework를 적용하여 로그인의 API를 구성하는 방법에 대해서 공유합니다. 1) 개발환경 💡 Spring Security 개발 환경을 구성하기 위해 사용한 개발환경입니

adjh54.tistory.com

 

[Java] Spring Boot Security 이해하기 -2 : 5.7.x 버전 구현하기

해당 글에서는 이전에 Spring Security 5.7.x 버전에 대해 이해한 내용을 기반으로 실제 구현하는 방법에 대해서 공유합니다. [참고] Spring Boot Security를 적용하기 이전의 이해하기 위한 글을 참고하시

adjh54.tistory.com

 

 

 

 

 

 

 

 

오늘도 감사합니다 😄

 

 

 

 

 

 

그리드형