반응형
해당 글에서는 Spring Security의 인증을 위한 ‘JWT: JSON Web Token’를 이해하고 적용하기 위해 우선 이해를 목적으로 작성한 글입니다. 추후 적용을 위한 환경 설정 방법에 대해서 공유합니다.
💡 [참고] 해당 글에서는 이전에 작성한 Spring Boot Security 기반으로 추가 구성합니다.
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를 구성한 값을 인코딩하여서 사용이 됩니다.
이에 대해서 각각을 확인 해 봅니다.
[참고] 공식 사이트
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 웹 토큰의 마지막 세 번째로 요소로 사용이 됩니다.
[참고] 구성한 서명을 아래의 디버깅을 통해 디코딩을 수행 할 수 있습니다.
[참고] 서명 생성 예시
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에 대한 구현 내용은 아래의 글에서 이어집니다.
[참고] 사용자 정보를 전달하기 전에 Spring Security의 구성을 이해하시면 이후 환경설정에 도움이 됩니다.
오늘도 감사합니다 😄
반응형
'Java > Spring Boot' 카테고리의 다른 글
[Java] Spring Boot Tomcat Access Log 이해하고 설정하기 (0) | 2023.02.26 |
---|---|
[Java] Spring Boot Security 이해하기 -4: JWT 환경 설정 및 구성 하기 (4) | 2023.01.01 |
[Java] Spring Boot Security 이해하기 -2 : Spring Boot 2.x 버전 환경 구성하기 (0) | 2022.12.18 |
[Java] Spring Boot Security 이해하기 -1 : 2.7.x 버전 구조 및 파일 이해 (0) | 2022.12.18 |
[Java] Business Exception 이해하고 구성하기 : Service Exception (0) | 2022.12.10 |