오늘도 개발
인가(Authorization) - SSS, JWT 본문
1. 개요
인가란?
해당 유저가 요청한 페이지에 권한이 있는지 확인하는 절차.
(예 : 1번 유저는 2번 유저의 장바구니에 접근할 수 없게 하는 것)
HTTP 통신에서 인가할 때의 문제점
Http는 stateless(통신 상태를 저장하지 않음)하기 때문에
Http만으로는 인가를 매끄럽게 진행할 수 없다.
예를 들면 아래와 같은 상황이 발생할 수 있다.
<첫 번째 통신>
- 요청 1 : 로그인 해주세요.
- 응답 1 : OK
<두 번째 통신>
- 요청 2: 장바구니 화면을 주세요.
- 응답 2: 로그인 안 되어 있습니다. (첫 번째 통신의 내용을 모름)
이런 경우 유저는 로그인된 유저만 접근할 수 있는 페이지를 요청할 때마다
다시 로그인을 해야 한다.
<첫번째 통신>
- 요청 1 : 로그인 해주세요.
- 응답 1 : OK
<두번째 통신>
- 요청 2: 로그인 하고 장바구니 화면을 주세요.
- 응답 2: OK
하지만 로그인은 암호화 과정이 필요하므로 무겁고 오래 걸린다.
화면을 요청할 때마다 로그인 해야 한다면 굉장히 비효율적일 것이다.
이를 해결하기 위해 세션(SSS), 토큰(JWT) 등 다양한 방법을 활용할 수 있다.
2. SSS(Server Side Sessions)
sss란?
세션으로 인가를 진행하는 방법.
세션 ID를 서버 컴퓨터의 메모리에 저장한다.
<로그인 시>
1) 클라이언트가 로그인 요청
2) 서버가 DB 확인 후 인증 완료
3) 세션 ID를 서버 컴퓨터의 메모리에 저장
<장바구니 접속 시>
1) 클라이언트가 장바구니 페이지 요청
2) 서버가 메모리에서 세션 ID 확인
3) 메모리에 세션 ID가 있으면 장바구니 페이지 반환

문제점
확장성이 좋지 않다.
유저 수가 많아지면 세션 아이디를 모두 저장할 수 있도록 서버 컴퓨터의 용량을 늘려야 한다.
이 때 용량은 두 가지 방법으로 늘릴 수 있다.
1) vertical scaling : 서버 컴퓨터 한 대의 메모리 용량 증가
2) horizontal scaling : 여러 컴퓨터를 서버 컴퓨터로 사용하고 load balancer 설치
보통 두 번째 방법으로 용량을 늘리는데, 이 때 문제가 발생한다.
load balancer가 세션 아이디가 저장되지 않은 서버 컴퓨터로 요청을 보내는 경우
유효한 유저라도 인증, 인가에 실패하게 된다.
이를 해결하기 위해 모든 서버 컴퓨터의 싱크를 맞출 수는 있는데 과정이 번거롭고 에러가 발생하기 쉽다.
데이터베이스를 추가할 수도 있지만 비용 문제가 발생한다.
이런 문제를 해결하기 위해 JWT가 등장했다.
3. JWT(JSON Web Tokens)
JWT이란?
토큰으로 인가를 진행하는 방법이다.
SSS보다 확장성이 좋다.
최초 로그인 시 서버가 암호화된 토큰(JSON Web Token)을 생성하여 클라이언트로 보내준다.
이후 클라이언트는 서버에게 요청을 보낼 때 이 토큰을 넣어 보낸다.
서버는 클라이언트에게 토큰을 받으면 복호화한 후 인가를 진행한다.
<첫번째 통신>
요청 1 : 로그인 해주세요
응답 1 : 로그인 OK, 토큰 보내줌
<두번째 통신>
요청 2: 장바구니 접속해주세요, 토큰 첨부
응답 2: 토큰 확인, 접속 OK
JWT 구조
1) Header
해당 토큰에 대한 정보 (토큰 종류, Signature 부분에서 암호화할 때 사용한 해시 알고리즘 이름 등).
Base64 방식으로 인코딩 되어있다.
예 : { "alg" : "HS256", "typ" : "JWT" }
2) Payload
실제로 인가에 필요한 내용 (유저의 pk, 토큰 만료기한 등).
Base64 방식으로 인코딩 되어있다.
인코딩은 암호화가 아니기 때문에 누구나 쉽게 열어볼 수 있다.
따라서 payload에는 절대 중요한 개인정보(비밀번호 등)를 넣으면 안 된다.
토큰에 담는 name, value 한 쌍은 클레임(claim)이라고 한다.
payload에 담을 수 있는 클레임은 다음과 같다.
- registered claim(name이 등록된 클레임) : iss(토큰 발급자), sub(토큰 제목), exp(토큰 만료시간) ...
- public claim(공개 클레임) : uri로 지음
- private claim(비공개 클레임) : 클라이언트, 서버 합의 하에 사용하는 이름
예 : { "id" : 3, "exp" : 1540517302 }
3) Signature
올바른 서버에서 발급한 JWT가 맞다는 것을 알려주는 부분.
인코딩된 header와 payload를 연결한 문자열에 JWT 시크릿 키를 추가하여
해시 알고리즘(예: HS256)에 넣어 얻은 값이다.
Base64 방식으로 인코딩 되어있다.
jwt 발급하기
1) pyjwt 설치
pip3 install pyjwt
2) 시크릿 키와 알고리즘명 환경 변수로 만들기(외부에 노출되면 안되기 때문)
<my_settings.py>
환경변수 저장
# my_settings.py
# my_settings.py는 manage.py가 있는 위치에 있어야 함
# my_settings.py는 .gitignore에 포함시켜야 함
# 환경변수명과 값 저장
JWT_SECRET='secret'
JWT_ALGORITHM='HS256'
DEBUG=True
<settings.py>
my_settings의 환경변수를 settings.py에 임포트.
views.py에 바로 my_settings를 임포트하면 환경변수명이 바뀔 시 모든 views.py를 수정해야 함.
my_settings의 환경변수를 settings.py에 임포트하고, views.py가 settings.py를 임포트하면
환경변수명을 바꿔도 settings.py만 수정하면 됨.
from my_settings import DATABASES, JWT_SECRET, JWT_ALGORITHM
3) jwt 생성 : 첫번째 인자는 payload에 담을 내용, 두번째 인자는 시크릿 키, 세번째 인자는 사용할 알고리즘
import jwt
from django.conf import settings
# 토큰 생성
access_token = jwt.encode({'id' : 5}, settings.'JWT_SECRET', algorithm = settings.'JWT_ALGORITHM')
# 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyLWlkIjo1fQ.tBQu0HfnOYK7lL3tH5ImgsI-y4Jz1RKscJWbV3U2QMI'
return JsonResponse({'message': 'SUCCESS', 'access_token': access_token}, status = 200)
클라이언트가 보낸 jwt 확인하기
encoded_jwt = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyLWlkIjo1fQ.tBQu0HfnOYK7lL3tH5ImgsI-y4Jz1RKscJWbV3U2QMI'
jwt.decode(encoded_jwt, settings.'JWT_SECRET', algorithms='HS256')
print(jwt.decode)
# {'user-id' : 5}
참고
[인가] access token / JWT / 데코레이터 / 토큰 만료 시간
Why do we need the JSON Web Token(JWT) in the Modern Web Era
'웹 프로그래밍 > 웹의 이해' 카테고리의 다른 글
| [AWS] 클라우드 컴퓨팅과 AWS 개요 (0) | 2022.07.28 |
|---|---|
| RESTful API (0) | 2022.07.20 |
| 인증(Authentication) - Bcrypt (0) | 2022.07.11 |
| 인터넷과 웹의 역사 (0) | 2022.06.21 |
| URI와 URL의 차이 (0) | 2022.06.04 |
