안녕하세요 놀이방 사장입니다.
이번 포스팅에서는
토큰 기반 인증에 대해 알아보겠습니다.
토큰 기반 인증이란?
사용자가 서버에 접근할 떄 사용자가 인증된 사용자인지 확인하는 방법은 다양하다
대표적으로 1. 서버 기반 인증 2. 토큰 기반 인증이 있다.
스프링 시큐리티에서는 기본적으로 세션기반 인증을 제공해줍니다.
그 중 토큰 기반 인증은 토큰을 사용하는 방법입니다.
토큰은 서버에서 클라이언트를 구분하기 위한 유일한 값이다.
서버가 토큰을 생성해서 클라이언트에게 제공하면 클라이언트는 이 토큰을 가지고 있다가 여러 요청을 이 토큰과 함께 신청 => 서버는 토큰만 보고 유효한 사용자인지 확인
토큰을 전달하고 인증하는 과정
1. 로그인 요청
클라이언트 -> 서버
클라이언트가 아이디와 비밀번호를 서버에게 전달하면서 인증을 요청
2. 토큰 생성 후 응답
서버 -> 클라이언트
서버는 아이디와 비밀번호를 확인해서 유효한 사용자인지 검증
3. 토큰저장
클라이언트
클라이언트는 서버에서 준 토큰을 저장한다.
4. 토큰 정보와 함께 요청
클라이언트->서버
이후 인증이 필요한 API를 사용할 때 토큰을 함께 보낸다.
5. 토큰 검증
서버
서버는 토큰이 유효한지 검증
6. 응답
서버 -> 클라이언트
토큰이 유효하다면 클라이언트가 요청한 내용을 처리
토큰 기반 인증의 특징
토큰 기반의 인증은 크게 3가지 무상태성 , 확장성, 무결성이라는 특징을 가지고 있다.
1. 무상태성
무상태성은 사용자의 인증 정보가 담겨있는 토큰이 서버가 아닌 클라이언트에 있으므로 서버에 저장할 필요가 없다.
서버가 뭔가 데이터를 유지하고 있으려면 그만큼 자원을 소비해야함
토큰 기반 인증에서는 클라이언트에서 인증 정보가 담긴 토큰을 생성하고 인증한다.
따라서 클라이언트에서 사용자의 인증 상태를 유지하면서 이 후 요청을 처리해야하는데 이것을 상태를 관리한다고 한다.
이렇게 되면 서버에서는 클라이언트의 인증정보를 저장하거나 유지하지 않아도 되므로 완전한 무생타로 효율적인 검증을 할 수 있다.
2. 확장성
무상태성은 확장성에 영향을 준다.
서버를 확장할 떄 상태 관리를 신경 쓸 필요가 없으므로 서버 확장에 유리하다
EX)
물건을 파는 서비스하는 서버, 결제를 위한 서비스 두 개의 서버가 분리되어 있을 떄 세션 인증 기반은 각각 API에서 인증을 해야한다. 하지만 토큰 기반 인증에서는 토큰을 가지는 주체가 서버가 아니라 클라이언트이기 떄문에 하나의 토큰으로 결제, 주문 서버에 요청을 보낼 수 있다.
추가로 페이스북 로그인, 구글 로그인같이 토큰 기반 인증을 사용하는 다른 시스템에 접근해 로그인을 확장 할 수 있으며 이를 활용해 다른 서버스의 권한을 공유할 수 있다.
3. 무결성
토큰 방식은 HMAC(hash-based message authentication) 기법이라고도 부른다.
토큰을 발급한 이후에는 토큰 정보를 변경하는 행위를 할 수 없다. => 무결성 보장
만약 누군가 한 글자라도 변경하면 서버는 유효하지 않은 토큰이라고 판단한다.
JWT에 대해 알아보자
발급받은 JWT를 이용해서 인증을 하려면 HTTP 요청 헤더 중에 Authorization 키값에 Bearer + JWT 토큰값을 넣어 보내야한다.
JWT의 구조 알아보기
JWT는 어떤 구조로 이루어져 있을까?
.을 기준으로 헤더, 내용, 서명을 구성되어 있다.
aaaaaa . bbbbbb. cccccc
-- 헤더 . 내용 . 서명
헤더에는 토큰의 타입과 해싱 알고리즘을 지정하는 정보를 담는다.
{
"typ" : "JWT",
"alg" : "HS256"
}
=> JWT 토큰, HS256 해싱 알고리즘을 사용한다는 뜻
이름 | 설명 |
typ | 토큰의 타입을 지정, JWT 라는 문자열이 들어간다. |
alg | 해싱 알고리즘을 지정한다. |
내용에는 토큰과 관련된 정보를 담는다.
내용의 한덩어리를 클레임이라고 부르고 클레임은 키값의 한 쌍으로 이루어져 있다.
클레임은 등록된 클레임, 공개 클레임, 비공개 클레임으로 나눌 수 있다.
등록된 클레임은 토큰에 대한 정보를 담는데 사용한다.
이름 | 설명 |
iss | 토큰 발급자(issuer) |
sub | 토큰제목(subject) |
aud | 토큰 대상자(audience |
exp | 토큰의 만료시간(expiration), 시간은 NumbericDate형식으로 하며 , 항상 현재 시간 이후로 설정된다. |
nbf | 토큰의 활성 날짜와 비슷한 개념으로 nbf는 Not Before를 의미, NumbericDate 형식으로 날짜를 지정하며 이 날짜가 지나가기 전 까지는 토큰이 처리되지 않음 |
iat | 토큰이 발급된 시간으로 iat 는 issued at을 의미한다. |
jti | JWT의 고유 식별자로서 주로 일회용 토큰에 사용한다. |
공개 클레임은 공개되어도 상관없는 클레임을 의미
충돌을 방지할 수 있는 이름을 가져야하고 보통 클레임이름을 URL로 ㅈㅅ는다
비공개클레임은 공개되면 안되는 클레임을 의미한다.
클라이언트와 서버간의 통신에 사용된다.
{
"iss" : "123@naver.com", //등록된 클레임
"iat" : 1622370813, //등록된 클레임
"exp" : 1622380713, //등록된 클레임
"https://naver.com/jwt_claim//is_admin" : true, // 공개클레임
"email : "123@naver.com", // 비공개클레임
"hi : ki" // 비공개 클레임
}
서명은 해당 토큰이 조작되었거나 변경되지 않았음을 확인하는 용도로 사용하고 헤더의 인코딩 값과 내용의 인코딩값을 합친 후에 비밀키를 사용해 해시값을 생성
토큰의 유효기간
만약 토큰을 주고 받는 환경이 보안에 취약하여 토큰 자체가 노출되면 문제가 생긴다.
어떻게 하면 이문제를 막을 수 있나?
토큰은 이미 발급되면 그 자체로 인증 수단이되므로 서버는 토큰과 함꼐 들어온 요청이 토큰을 탈취한 사람의 요청인지 확인 할 수 없다.
여기서 나온게 "리프레시 토큰"
리프레시 토큰이 있다면??
토큰의 유효기간이 하루라면 하루동안 그 토큰으로 무엇이든 할 수 있기에 치명적이고 유효기간이 너무 짧으면 사용자 입장에서 너무 불편함
=> 이 지점을 해결하기 위해 리프레시 토큰이 등장
리프레시 토큰이 등장-> 액세스 토큰과 별개의 토큰
사용자를 인증하기 위한 용도가 아닌 액세스 토큰이 만료되었을 떄 새로운 액세스 토큰을 발급하기 위해 사용된다.
액세스 토큰의 유효기간을 짧게 설정하고 리프레시 토큰의 유효 기간을 길게 설정하면 공격자가 액세스 토큰을 탈취해도 몇분뒤면 사용하지 못하기에 보안이 더 좋아진다.
로직 설명
1. 인증요청
클라이언트 -> 서버
클라이언트가 서버에 인증을 요청
2. 액세스토큰 & 리프레시 토큰 응답
서버 -> 클라
서버는 클라이언트에서 전달한 정보를 바탕으로 인증정보가 유효한지 확인 후 액세스 토큰과 리프레시 토큰을 만들어서 클라이언트에게 전달, 클라이언트는 전달받은 토큰을 저장한다.
3. 리프레시 토큰 저장
서버 -> 디비
서버에서 생성한 리프레시 토큰은 디비에도 저장
4. 요청
클라 -> 서버
인증을 필요로하는 API를 호출할 떄 클라이언트에 저장된 액세스 토큰과 함께 API를 요청한다.
5. 토큰 유효성 검사 & 응답
서버는 전달받은 액세스 토큰이 유효한지 검사한 뒤에 유효하다면 클라이언트에서 요청한 내용을 처리
6. (만료된 엑세스 토큰과 함께) 요청
클라 -> 서버
시간이 지나고 액세스 토큰이 만료된 뒤에 클라이언트에서 원하는 정보를 얻기 위해 서버에게 API를 요청한다.
7. 토큰 만료 응답
서버 -> 클라
서버는 액세스 토큰이 유효한지 검사를 한다. 만료된 토큰이면 유효하지 않기 때문에 토큰이 만료되었다는 에러를 전달한다.
8. (리프레시 토큰과 함께) 액세스 토큰 발급 요청
클라 -> 서버
클라이언트는 이 응답을 받고 저장해둔 리프레시 토큰과 함꼐 새로운 액세스 토큰을 발급하는 요청을 한다.
9. 리프레시 토큰 조회&유효성 검사
서버 -> 디비
서버는 전달받은 리프레시 토큰이 유효한지 디비에서 리프레시 토큰을 조회한 후 저장해둔 리프레시 토큰과 같은지 확인한다.
10. 새로운 액세스 토큰 응답
서버-> 클라
만약 유효한 리프레시 토큰이라면 새로운 액세스 토큰을 생성한 뒤 응답
그 이후 클라이언트는 4번과 같이 API호출
이상으로 JWT, 토큰 기반 인증에 대해 알아보았습니다.
'웹 > Spring' 카테고리의 다른 글
스프링 JWT 서비스 구현하기 (0) | 2023.11.19 |
---|---|
스프링부트3 회원가입 로그인, 로그아웃 구현하기 - 로그인, 로그아웃 서비스 메서드 작성하기 (0) | 2023.10.24 |
스프링부트3 회원가입 로그인, 로그아웃 구현하기 - 회원 도메인 만들기 (0) | 2023.10.23 |
스프링부트3 회원가입 로그인, 로그아웃 구현하기 - 사전지식 스프링 시큐리티 (0) | 2023.10.23 |
스프링부트3 블로그 글 삭제 기능 추가하기 (0) | 2023.10.22 |