'인증'과 '인가'
인증(Authentication)
- 식별 가능한 정보로 서비스에 등록된 유저의 신원을 입증하는 과정
- 사용자가 누구인지 확인하는 절차이며 쉽게 얘기해서 로그인하는 과정
인가(Authorization)
- 인증된 사용자에 대하여 자원의 접근권한을 확인하는 것
- 사용자에 대한 권한을 허락
JWT(Json Web Token)
JSON 객체를 사용해서 토큰 자체에 정보들을 저장하고 있는 Web Token
JWT 토큰을 전달하고 인증 받는 과정
1. 클라이언트가 서버에 로그인 요청을 한다.
2. 서버는 아이디와 비밀번호를 확인해 유효한 사용자인지 검증한다.
3. 유효한 사용자이면 토큰을 생성해서 응답한다.
4. 클라이언트는 서버에서 준 토큰을 저장한다.
5. 이후 인증이 필요한 API를 사용할 때 토큰을 함께 보낸다.
6. 서버는 토큰이 유효한지 검증한다.
7. 토큰이 유효하다면 클라이언트가 요청한 내용을 처리한다.
발급 받은 JWT를 이용해 인증을 하려면 HTTP 요청 헤더 중에 Authorization 키 값에 Bearer + JWT token 값을 넣어 보내면 된다.
JWT 구성
JWT는 '.' 을 기준으로 헤더(header), 내용(payload), 서명(signature)으로 이루어져 있다.
헤더(header)
- 토큰 타입과 해싱 알고리즘을 지정하는 정보를 담는다.
- typ : 토큰의 타입을 지정한다.
- alg : 해싱 알고리즘을 지정한다.
{ "typ" : "JWT", "alg" : "HS256" } // 위의 경우 JWT 토큰과 HS256 해싱 알고리즘을 사용한다는 내용이다.
내용(payload)
- 토큰과 관련된 정보를 담는다.
- 내용의 한 덩이를 클레임(claim)이라고 부르며, 클레임은 키-값의 한 쌍으로 이루어져 있다.
- 클레임은 등록된 클래임, 공개 클레임, 비공개 클레임으로 나눌 수 있다.
1. 등록된 클레임
- 토큰에 대한 정보를 담는데 사용한다.
- iss : 토큰 발급자(issuer)
- sub : 토큰 제목(subject)
- aud : 토큰 대상자(audience)
- exp : 토큰의 만료시간(expiraton). 시간은 NumericDate 형식으로 하며, 항상 현재시간 이후로 설정
- nbf : 토큰의 활성 날짜와 비슷한 개념(not before). NumericDate 형식으로 날짜를 지정하며, 이 날짜가 지나가기 전까지는 토큰이 처리되지 않는다.
- iat : 토큰이 발급된 시각(issued at)
- jti : JWT 고유 식별자로서 주로 일회용 토큰에 사용
2. 공개 클레임
- 공개 되어도 상관 없는 클레임을 의미한다.
- 충돌을 방지할 수 있는 이름을 가져야 하며, 보통 클레임 이름을 URI로 짓는다.
3. 비공개 클레임
- 공개되면 안되는 클레임이며 클라이언트와 서버간의 통신에 사용된다.
{ "iss" : "test@test.com", // 등록된 클레임 "iat" : 1622370878 // 등록된 클레임 "exp" : 1622370878 // 등록된 클레임 "https:// ~ " : true, // 공개 클레임 "email" : "test@test.com", // 비공개 클레임 "hello" : "안녕하세요!" // 비공개 클레임 }
서명(signature)
- 해당 토큰이 조작되었거나 변경되지 않았음을 확인하는 용도로 사용
- 헤더의 인코딩 값과 내용의 인코딩 값을 합친 후에 주어진 비밀 키를 사용해 해시 값을 생성한다.
리프레시 토큰 (refresh token)
- 액세스(access token) 토큰과는 별개이며, 사용자를 인증하기 위한 용도가 아닌, 액세스 토큰이 만료 되었을 때 새로운 액세스 토큰을 발급하기 위해 사용
- 액세스 토큰의 유효시간을 짧게 설정하고, 리프레시 토큰의 유효 기간을 길게 설정하면 공격자가 액세스 토큰을 탈취해도 몇 분 뒤에는 사용할 수 없는 토큰이 되어 버려서 안전하다.
1. 클라이언트가 서버에게 인증 요청을 한다.
2. 서버는 클라이언트에서 전달한 정보를 바탕으로 인증 정보가 유효한지 확인한 뒤, 액세스 토큰과 리프레시 토큰을 만들어 클라이언트에게 전달한다.
3. 클라이언트는 전달 받은 토큰을 저장한다.
4. 서버에서 생성한 리프레시 토큰은 DB에도 저장해둔다.
5. 인증을 필요로하는 API를 호출할 때 클라이언트에서 저장된 액세스 토큰과 함께 API를 요청한다.
6. 전달받은 액세스 토큰이 유효한지 검사한뒤에 유효하다면 클라이언트에서 요청한 내용을 처리한다.
7. 시간이 지나고 액세스 토큰이 만료된 뒤에 클라이언트에서 원하는 정보를 얻기위해 서버에게 API 요청을 보낸다.
8. 서버에서 액세스 토큰이 유효한지 검사한다. 만료된 토큰이면 유효하지 않기 때문에 토큰이 만료되었다는 에러를 전달한다.
9. 클라이언트에서는 이 응답을 받고 저장해둔 리프레시 토큰과 함께 새로운 액세스 토큰을 발급하는 요청을 전송한다.
10. 서버에서는 전달받은 리프레시 토큰이 유효한지, DB에서 리프레시 토큰을 조회한 후 저장해둔 리프레시 토큰과 같은지 확인한다.
11. 만약 유효한 리프레시 토큰이라면 새로운 액세스 토큰을 생성한 뒤 응답한다.
12. 그 이후에 클라이언트는 5번과 같이 다시 API를 요청한다.
참고 도서 : 스프링 부트 3 백엔드 개발자 되기: 자바 편 | 신선영