이전글
[개인프로젝트/프로그] 3. 스프링 시큐리티로 회원가입, 로그인/로그아웃 구현하기
1. 개요인증과 인가인증(authentication)보호된 리소스에 접근하는 것을 허용하기 이전에, 등록된 사용자의 신원을 입증하는 과정ex) 로그인인가(authorization)특정 부분에 접근할 수 있는 권한이 있는
progfrog.tistory.com
토큰 기반 인증이란?
- 사용자가 서버에 접근할 때 이 사용자가 인증된 사용자인지 확인하는 방법은 다양하다.
- 대표적인 사용자 인증 확인 방법은 2가지
- 세션 기반 인증
- 토큰 기반 인증
- 세션 기반 인증
- 사용자마다 사용자의 정보를 담은 세션을 생성하고, 저장해서 인증
- 토큰 기반 인증
- 토큰은 서버에서 클라이언트를 구분하기 위한 유일한 값
- 서버가 토큰을 생성해서 클라이언트에게 제공하면, 클라이언트는 이 토큰을 가지고 있다가 여러 요청을 이 토큰과 함께 신청
- 서버는 토큰만 보고 유효한 사용자인지 검증한다.
토큰을 전달하고 인증받는 방법
- 클라이언트가 아이디와 비밀번호를 서버에게 전달하면서 인증 요청
- 서버는 아이디와 비밀번호를 확인해 유효한 사용자인지 검증하고, 유효한 사용자라면 토큰을 생성해서 응답
- 클라이언트는 서버에서 전달해 준 토큰을 저장
- 이후 인증이 필요한 API를 사용할 때 토큰을 함께 보냄
- 서버는 해당 토큰이 유효한지 검증
- 토큰이 유효하다면 클라이언트가 요청한 내용을 처리
토큰 기반 인증 특징
무상태성
- 사용자의 인증 정보가 담겨 있는 토큰이 서버가 아닌 클라이언트에 있으므로 서버에 저장할 필요가 없음
- 서버 입장에서는 클라이언트의 인증 정보를 저장하거나 유지하지 않아도 되기 때문에 완전한 무상태(stateless)로 효율적인 검증 가능
확장성
- 무상태성은 확장성에 영향을 준다.
- 서버를 확장할 때 상태 관리를 신경 쓸 필요가 없으므로 확장에도 용이하다.
- 결제를 위한 서버와 주문을 위한 서버가 분리되어 있다고 할 때
- 세션 기반 인증은 각각 API에서 인증을 해야 함
- 토큰 기반 인증은 토큰을 가지는 주체가 서버가 아니라 클라이언트이기 때문에 가지고 있는 하나의 토큰으로 결제 서버와 주문 서버에게 요청을 보낼 수 있음!
- 페이스북 로그인, 구글 로그인 같이 토큰 기반 인증을 사용하는 다른 시스템에 접근해 로그인 방식을 확장하거나, 이를 활용해 다른 서비스에 권한을 공유할 수도 있음
무결성
- 토큰 방식은 HMAC(hash-based message authentication) 기법이라고도 함
- 토큰을 발급한 이후에는 토큰 정보를 변경하는 행위를 할 수 없음! 즉, 토큰의 무결성이 보장
- 누군가 토큰을 한 글자라도 변경하면, 서버에서는 유효하지 않은 토큰이라고 판단
JWT
JWT.IO
JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties.
jwt.io
- Json Web Token
- JSON 객체를 사용하여 양 당사자 간에 정보를 안전하게 전송하기 위한 개방형 표준
- 주로 인증 및 권한 부여를 위해 사용됨
- JWT은 세 부분으로 구성되어 있으며, 각 부분은 마침표(.)로 구분됨
aaaaa . bbbbb. ccccc
- 헤더 . 페이로드(내용) . 서명
발급받은 JWT를 이용해 인증을 하려면 HTTP 요청 헤더 중에 Authorization 키 값에 Bearer + JWT 토큰값을 넣어 보내야 한다.
토근 기반 인증에서 bearer는 무엇일까?
본 글은 MDN - HTTP 인증, Veloport님의 게시글을 참고하여 작성되었습니다. 자세하게 알고싶으신 분은 해당 링크를 참고해주세요.토큰 기반 인증인증 타입마치며토큰 기반 인증은 쿠키나 세션을 이
velog.io
JWT의 구조
1. 헤더(Header)
- 헤더는 두 부분으로 구성됨
{
"typ": "JWT"
"alg": "HS256",
}
이름 | 설명 |
typ | 토큰의 타입을 지정하며, JWT라는 문자열이 들어감 |
alg | 해싱 알고리즘을 지정 |
2. 페이로드(Payload)
- 페이로드에는 클레임(claim)이라고 하는 여러 정보가 포함됨
- 클레임은 JWT 안에 포함된 정보 단위로 키값의 한 쌍으로 이루어져 있다.
- 등록된 클레임(registered claims)
- 토큰에 대한 정보를 담는 데 사용
- 하단 표 참고!
- 공개 클레임(public claims)
- 공개되어도 상관없는 클레임을 의미
- 충돌을 방지할 수 있는 이름을 가져야 하며, 보통 클레임 이름을 URI로 짓는다.
- 비공개 클레임(private claims)
- 공개되면 안 되는 클레임
- 클라이언트와 서버 간의 통신에 사용
- 등록된 클레임(registered claims)
이름 | 설명 |
iss | 토큰 발급자(issuer) |
sub | 토큰 제목(subject) |
aud | 토큰 대상자(audience) |
exp | 토큰의 만료 시간(expiration) 시간은 NumbericDate 형식(ex. 14800849147370)으로 하며, 항상 현재 시간 이후로 설정 |
nbf | 토큰의 활성 날짜와 비슷한 개념으로 nbf는 Not Before를 의미 NumbericDate 형식으로 날짜를 지정하며, 이 날짜가 지나기 전까지는 토큰이 처리되지 않음 |
iat | 토큰이 발급된 시간(issued at) |
jti | JWT의 고유 식별자로서 주로 일회용 토큰에 사용 |
예를 들어, 다음과 같은 JWT의 내용이 있다고 하자.
{
"iss": "test@email.com", // 등록된 클레임
"iat": 1622370878, // 등록된 클레임
"exp": 1622372678, // 등록된 클레임
"https://progfrog.tistory.com/jwt_claims/is_admin": true, // 공개 클레임
"email": "a@email.com", // 비공개 클레임
"hello": "안녕하세요!" // 비공개 클레임
}
- iss, iat, exp는 JWT 자체에서 등록된 클레임
- URI로 네이밍 된 클레임은 공개 클레임
- 등록된 클레임도, 공개 클레임도 아닌 email, hello는 비공개 클레임
3. 서명(Signature)
- 서명은 해당 토큰이 조작되었거나 변경되지 않았음 즉, 무결성을 확인하는 용도로 사용
- 헤더와 페이로드를 인코딩한 후 비밀키를 사용해서 해시값을 생성
서명 생성 예
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret
)
JWT 장점
- 자기 포함(self-contained)
- JWT는 필요한 모든 정보를 자체적으로 포함하고 있어, 추가적인 데이터베이스 조회 없이도 정보를 확인할 수 있다.
- 확장성
- JWT는 다양한 클레임을 포함할 수 있어 유연하게 사용할 수 있다.
- 보안성
- 서명된 JWT는 클라이언트에서 수정되지 않았음을 확인할 수 있다.
- HMAC 또는 RSA를 사용하여 서명할 수 있다.
HMAC과 RSA
HMAC(Hash-based Message Authentication Code)개념HMAC는 암호화 해시 함수와 비밀 키를 사용하여 메시지의 무결성과 인증을 보장하는 방식주로 메시지의 무결성을 확인하고, 송신자의 인증을 보장하기 위해
progfrog.tistory.com
JWT 사용 예시
- 인증
- 사용자가 로그인할 때 JWT를 발급받아 클라이언트에 저장하고, 이후 요청 시 이 토큰을 첨부하여 인증을 처리
- 정보 교환
- 클라이언트와 서버 간에 필요한 정보를 안전하게 전송할 때 사용
JWT 구조 예시
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
- 헤더: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
- 페이로드: eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
- 서명: SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
토큰 유효기간
- 그런데 만약 토큰을 주고받는 환경이 보안에 취약해서 토큰 자체가 노출되면 어떻게 될까?
- 토큰은 발급되면 이미 그 자체로 인증 수단이 되므로, 서버는 토큰과 함께 들어온 요청이 토큰을 탈취한 사람의 요청인지 확인할 수 없다.
리프레시 토큰
- 토큰의 유효기간이 하루라면 어떨까? 하루 동안은 그 토큰으로 무엇이든 할 수 있을 테니 큰일이다!
- 토큰의 유효기간을 짧게 하자!
- 하지만 토큰의 유효기간이 짧으면 사용자 입장에서는 너무 짧은 시간 동안만 이 토큰을 활용할 수 있다...
- 리프레시 토큰 두둥 등장
- 리프레시 토큰은 액세스 토큰과 별개의 토큰
- 사용자를 인증하기 위한 용도가 아닌, 액세스 토큰이 만료되었을 때 새로운 액세스 토큰을 발급하기 위해 사용
- 액세스 토큰의 유효 기간을 짧게 설정하고, 리프레시 토큰의 유효 기간을 길게 설정하면 공격자가 액세스 토큰을 탈취해도 몇 분 뒤에는 사용할 수 없는 토큰이 되므로 더 안전하다.
- 클라이언트가 서버에게 인증을 요청한다.
- 서버는 클라이언트에서 전달한 정보를 바탕으로 인증 정보가 유효한지 확인한 뒤, 액세스 토큰과 리프레시 토큰을 만들어 클라이언트에게 전달한다. 클라이언트는 전달받은 토큰을 저장한다.
- 서버에서 생성한 리프레시 토큰은 DB에도 저장해 둔다.
- 인증을 필요로 하는 API를 호출할 때, 클라이언트에 저장된 액세스 토큰과 함께 API를 요청한다.
- 서버는 전달받은 액세스 토큰이 유효한지 검사한 뒤에, 유효하다면 클라이언트에서 요청한 내용을 처리한다.
- 시간이 지나고...액세스 토큰이 만료된 뒤에 클라이언트에서 원하는 정보를 얻기 위해 서버에게 API 요청을 보낸다.
- 서버는 액세스 토큰이 유효한지 검사를 한다. 만료된 토큰이면 유효하지 않기 때문에 토큰이 만료되었다는 에러를 전달한다.
- 클라이언트는 이 응답을 받고 저장해 둔 리프레시 토큰과 함께 새로운 액세스 토큰을 발급하는 요청을 전송한다.
- 서버는 전달받은 리프레시 토큰이 유효한지, DB에서 리프레시 토큰을 조회한 후에 저장해 둔 리프레시 토큰과 같은지를 확인한다.
- 만약 유효한 리프레시 토큰이라면 새로운 액세스 토큰을 생성한 뒤 응답한다. 그 이후에 클라이언트는 4번과 같이 다시 API를 요청한다.
실습 예제
[개인프로젝트/프로그] 4. JWT로 로그인/로그아웃 구현하기
1. 개념 [스프링] 토큰 기반 인증과 JWT프로젝트 생성 토큰 기반 인증이란?사용자가 서버에 접근할 때 이 사용자가 인증된 사용자인지 확인하는 방법은 다양하다.대표적인 사용자 인증 확인 방
progfrog.tistory.com