1. JWT(JSON Web Token) 이란?
- JSON Web Token의 약자로 전자서명 된 URL-safe (URL로 이용할 수 있는 문자로만 구성된)의 JSON
- 전자서명(Signature)은 JSON의 변조를 체크할 수 있게 되어 있다.
- 속성 정보(Claims)를 JSON 데이터 구조로 표현한 토큰으로 RFC 7519 표준
- 서버와 클라이언트 간 정보를 주고 받을 때 HttpRequest header에 JSON 토큰을 넣은 후 서버는 별도의 인증 과정없이 헤더에 포함되어 있는 JWT 정보를 통해 인증한다.
이때 사용되는 JSON 데이터는 URL-safe 하도록 URL에 포함할 수 있는 문자만으로 만든다. - HMAC 알고리즘을 사용하여 비밀키 또는 RSA를 이용한 Public Key / Private Key 쌍으로 서명할 수 있다.
Base64 인코딩의 경우 "+", "/", "="이 포함되지만 JWT는 URI에서 파라미터로 사용할 수 있도록 URL-Safe 한 Base64url 인코딩을 사용한다.
2. JWT 구조
- Claims를 userId, username으로 하고, JWA 알고리즘 SHA-256으로 암호화
jwt:
secret: 12345678901234567890123456789000
public String createToken(Long userId, String name) {
return Jwts.builder()
.setHeaderParam("typ", Header.JWT_TYPE)
.signWith(key, SignatureAlgorithm.HS256)
.claim("userId", userId)
.claim("userName", name)
.setExpiration(new Date(System.currentTimeMillis() + 864000000))
.compact();
}
- 위 작업을 통해 생성된 토큰 구성
// Header
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.
// Payload
eyJ1c2VySWQiOjEsInVzZXJOYW1lIjoic2VvayIsImV4cCI6MTU5MzUyMTU0MX0.
// signature
AWsWjhGSeAIr9d0LGdDuFbGK57iK_mdsEnxt983h_1o
[Header]
- token의 type과 JWT를 digitally sign할 때 사용한 algorithm을 정의
- typ
- 토큰의 타입을 지정 ("JWT")
- alg
- 해싱 알고리즘을 지정한다.
- 해싱 알고리즘으로는 보통 HMAC SHA256 혹은 RSA가 사용되며, 이 알고리즘은 토큰을 검증할 때 사용되는 signature 부분에서 사용된다.
{
"typ" : "JWT",
"alg" : "HS256"
}
- 위 내용을 Base64url 인코딩 한 결과
// Header
eyJhbGciOiJIUzI1NiJ9.
[Payload]
-
JWT에 담아서 전달할 data를 정의
-
정보의 한 "조각"을 Claim이라 부르고, name / value의 한 쌍으로 이루어져있다.
-
Claim의 종류
-
Registered Claim
-
서비스에서 필요한 정보들이 아닌, 토큰에 대한 정보들을 담기 위하여 이름이 이미 정해진 Claim
-
모두 선택적(optional) 이다.
- iss: 토큰 발급자(issuer)
- sub: 토큰 제목(subject)
- aud: 토큰 대상자(audience)
- exp: 토큰의 만료시간(expiration)
- 시간은 NumericDate 형식으로 되어 있어야 한다.
- 언제나 현재 시간보다 이후로 설정되어 있어야 한다.
- nbf: Not Before을 의미한다.
- 토큰의 활성 날짜와 비슷한 개념
- NumericDate 형식으로 날짜를 지정하며, 날짜가 지나가기 전까지는 토큰이 처리되지 않는다.
- iat: 토큰이 발급된 시간(issued at)
- 이 값을 사용하여 토큰의 age가 얼마나 되었는지 판단 할 수 있다 .
- jti: JWT의 고유 식별자
- 주로 중복적인 처리를 방지하기 위하여 사용된다.
- 일회용 토큰에 사용하면 유용하다.
-
-
Public Claim
- 충돌이 방지된(collision-registant)이름을 가지고 있어야 한다.
- 충돌을 방지하기 위하여 Claim 이름을 URI 형식으로 짓는다.
-
Private Claim
- 서버 클라이언트 양측 간 협의하에 사용되는 Claim이름
- Public Claim과는 달리 이름이 중복될 수 있으니 사용시 유의
-
public String createToken(Long userId, String name) {
return Jwts.builder()
.setHeaderParam("typ", Header.JWT_TYPE)
.signWith(key, SignatureAlgorithm.HS256)
.claim("userId", userId)
.claim("userName", name)
.setExpiration(new Date(System.currentTimeMillis() + 864000000))
.compact();
}
- 위 내용을 암호화한 Payload
// Payload
eyJ1c2VySWQiOjEwMDQsInVzZXJOYW1lIjoiSm9obiJ9.
[Signature]
- JSON Web Token의 마지막 부분으로 Header와 Payload를 base64로 encode인코딩한 값을 합친 후 JWT secret key값을 encrypt한 값으로 Hash를 하여 생성
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
my-secret-key
) secret base64 encoded
- 위 내용으로 인코딩한 결과 값
// signature
0nwaeM3fpDPvRGc64pyIp-JYNnuigCN9t_5ApVhPClQ
JWT Token 예시
- 생성된 토큰은 HTTP 통신을 할 때 Authorization이라는 key의 value로 사용된다.
- 일반적으로 value에 Beare이 앞에 붙여진다.
{
"Authorization": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOjEsInVzZXJOYW1lIjoic2VvayIsImV4cCI6MTU5MzUyMTU0MX0.AWsWjhGSeAIr9d0LGdDuFbGK57iK_mdsEnxt983h_1o"
}
JWT Decoding
- JWT를 디코딩 해보는 테스트 사이트 https://jwt.io/
3. JWT를 사용하는 상황
- 회원 인증
- 사용자가 로그인을 하면, 서버는 사용자의 정보를 기반으로한 토큰을 발급
- 그 후, 사용자가 서버에 요청을 할 때마다 JWT를 포함하여 전달
- 서버는 클라이언트에서 요청을 받을 때 마다, 해당 토큰이 유효하고 인증되었는지 검증을 하고, 사용자가 요청한 작업에 권한이 있는지 확인하여 작업을 처리
- 서버에서는 사용자에 대한 세션을 유지할 필요가 없다.
즉, 사용자가 로그인되어 있는지 여부를 신경 쓸 필요가 없고, 사용자가 요청을 했을 때 토큰만 확인하면 되므로 세션 관리가 필요 없어서 서버 자원과 비용을 절감할 수 있다.
- 정보 교류
- JWT는 두 개체 사이에서 안정정있게 정보를 교환하기에 좋은 방법
- 정보가 서명이 되어있기 때문에 정보를 보낸이가 바뀌진 않았는지, 또 정보가 도중에 조작되지 않았는지 검증을 할 수 있다.
* UI Layer, REST API 서버를 따로 두는 경우 JWT를 사용
* 하나의 End Point가 아닌 Mobile / Web 등의 multiple EndPoint 환경이라면 통합적인 인증 / 인가 환경을 제공하기 위해 JWT를 사용
* Third Party에게 public하게 open 한 REST EndPoing가 존재하는 경우 해당 Third Party의 인증 인가를 관리하기 위해 JWT 사용
'Spring > SpringBoot' 카테고리의 다른 글
[Spring] JWT 기반 인증 API Server (0) | 2020.08.10 |
---|---|
[SpringSecurity] Authentication Process(인증 프로세스) (0) | 2020.07.04 |
[SpringBoot] 기존 테스트에 시큐리티 적용 (0) | 2020.06.02 |
[SpringBoot] OAuth2 Naver Login (2) | 2020.06.01 |
[SpringBoot] 세션 저장소로 DB이용하기 (0) | 2020.06.01 |