본문 바로가기

Java & Spring

spring + JWT + Angular 프로젝트 (1) - JWT란?

이번 프로젝트에서는 서버사이드의 Spring boot와 클라이언트 사이드의 Angular와의 연동시 로그인 및 API 인증을 위하여 전통적인 세션을 통한 관리를 하지 않고 Stateless한 RESTful API를 구현하기 위하여 JWT 기반의 인증을 이용할 것이다. 그에 먼저 JWT에 대해 간단히 정리하면 아래와 같다.


JWT (JSON Web Token) ?


JWT JSON 객체로서 안전하게 정보를 전송할 수 있는 작고 self-contained 방법을 정의하는 표준이다. 이 정보는 디지털로 서명되어 있기 때문에 신뢰할 수 있으며 HMAC 알고리즘 또는 RSA를 사용하는 공개 / 개인키 쌍을 사용하여 서명할 수 있다. 주로 회원 인증이나 작은 데이터 전달에 사용된다.



JWT 구조


JWT“.”을 구분자로 Header, Payload, Signature 세부분으로 이루어져 있다.

JWT = "header.payload.signature"

 

Header 


토큰이 어떤 암호화 알고리즘으로 암호화 되었는지 나타내는 필드와 토큰의 유형을 나타내는 필드로 이루어저진다.


Example: 

{
  "alg": "HS256",
  "typ": "JWT"
}

 

 

Payload


실제 전달하고자 하는 데이터를 Body로 나타내며 각 항목을 claim으로 부른다. Claim에는 Reserved claims, Public claims, private claims 3가지 종류가 존재한다.


- 예약된(Reserved) claims : JWT에서 미리 정의한 claim으로 필수는 아니지만 사용하기를 권장한다. sub(subject), exp(expiration time), iss(issuer)등이 존재한다.

 

- 공개(Public) claims : 공개 Claim은 사용자가 자유롭게 정의할 수 있다. 단 충동을 피하기 위하여 URI 형태로 정의되어야 한다.

 

- 비공개(Private) claims : 사용자 정의 claim으로 서버와 클라이언트간에 추가적으로 전달하고 싶은 데이터를 저장한다.

 

{
  "sub": "TEST",
  "name": "peter",
  "role": "ROLE_ADMIN,ROLE_USER"
}

 

Signature 


전달된 메시지에 대한 Signature 값을 나타낸다 Signature는 토큰을 보낸 발신자를 확인하고 JWT 메시지가 변경되지 않았는지 확인하는데 사용한다.



인코딩된 토큰을 보면 '.'을 구분자로 3부분으로 나눠진 것 을 확인할 수 있다.


eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJURVNUIiwicm9sZSI6IlJPTEVfQURNSU4sUk9MRV9VU0VSIiwibmFtZSI6InBldGVyIiwiZXhwIjoxNTA2MzQ5MTExfQ.bhtwUJkpuhuSX-olxmj5FmerKIC9liQqyhEZIQd-1bOenafQJ37Rm-VDtZOih9ioAgE6xyNGeCj7oxt8pC67aw




<JWT를 이용한 클라이언트와 서버 통신>


1. 공개된 Login API를 이용하여 로그인을 시도하고 성공시 JWT를 생성하여 클라이언트에 반환한다.


2. 클라이언트에서는 비공개된 API에 대해 요청시 Header에 JWT를 추가하여 요청한다.


3. 서버에서는 API의 비즈니스 로직을 실행하기 전에 클라이언트에서 전송한 JWT를 검증하고 검증시 성공하면 API를 실행하여 결과를 반환한다.


JWT 장점


-       JSON 형식으로 상대적으로 데이터의 크기가 작다.(SAML 토큰은 XML)

-       토큰에 데이터가 포함되어 있어 추가적인 데이터베이스 조회가 필요하지 않다.(Self-contained)

-       작은 애플리케이션에서 세션을 사용하지 않고 Stateless RESTful 서비스를 만들 수 있다.

 


JWT 단점


-       Header payloadbase64 인코딩으로만 암호화 되어 디코딩이 가능하기 때문에 중요한 데이터를 저장하면 안된다.

-       클라이언트에서 추가적으로 토큰을 저장 및 사용하고 만료 시간등을 체크하는 로직이 필요하다.

 


변조에 대한 우려


처음 JWT에 대해 공부할 때 토큰의 내용이 노출되기 때문에 토큰을 변조하여 서버에 요청하면 보안에 문제가 발생하지 않을까 생각했다. 하지만 Signature 비밀키는 클라이언트에 노출되지 않으며 클라이언트에서 발급된 JWT를 이용하여 서버에 요청할 시 서버에서 비밀키를 이용하여 유효성을 검증하기 때문에 클라이언트에서 변조를 하여 서버에 요청하더라도 문제가 발생하지 않는 것을 확인하였다. 그렇지만 클라이언트에서 디코딩하여 토큰의 내용을 확인할 수 있기 때문에 보안상 문제가 있는 데이터는 claims에 추가하지 않는 것이 좋다.



정리


JWT를 사용하면 RESTfulAPI 서비스에서 세션을 사용하지 않는 HTTP Stateless한 서비스를 구성할 수 있이며 자체적으로 데이터를 저장하기 때문에 백엔드 서버나 DB에 부담을 줄일 수 확장에 용의하다. 하지만 클라이언트 측에서 추가적으로 토큰을 관리해야 한다는 점과 JWTHeaderpayloadbase64 인코딩으로만 암호화 되기 때문에 디코딩이 가능하므로 중요한데이터를 저장하면 안된다. 그러므로 JWT를 사용할 때는 구성하는 프로젝트의 수준과 장단점을 고려해서 사용한다.