인증 & 인가
인증 : user의 id / pw을 확인하는 절차 -> password 단방향 해시 암호화 -> db에 저장 -> user id을 찾았다면 그 pw와 db에 있는 pw와 비교하여 일치하면 login -> login 성공하면 access totken을 클라이언트에 전송 -> 로그인 성공시 access token을 들고 request을 웹 서버에 전송하면서 매번 Login이 유도되지 않고 서비스를 이용할 수 있게됨.
인증이 로그인 하는 과정이라면, 인가는 로그인을 한 후에 사용자에게 권한을 부여하는 과정이다.
사용자 비밀번호 암호화 : 왜 사용자의 비밀번호를 디비에 저장할 대 암호화해서 저장해야할까? 외부의 해킹에 의해 디비가 노출이 될 수 있고, 내부 직원에 의해 노출될 수 있기 때문이다.
bcrypt
: 단방향 해시 암호 알고리즘은 복호화 할 수 있는 암호 알고리즘이 아니다. 이리되면 원본 값을 절대 복구 할 수 없을것 같아보이는데 천만의 말씀. 해킹은 쌉가능하다!!! 어케하니?
가장 널리 사용되는 방법은 rainbow attack이라는 방법이다. rainbow attack은 미리 해시값들을 계산해놓은 테이블을 생성한 후 역추적해서 본래 값을 찾는 방법이다. 이러한 문제점을 막기 위한 방법이 2가지가 있는데 salting과 key stretching이라는 방법이다.
salt가 소금이니까 양념치는것처럼 랜덤 데이터들을 더해서 해시화를 시키는 방법
, key stretching은 여러번의 해시값을 계산하는것. 이 2개를 잘 사용한게 비크립트이다
access token : 사용자가 로그인에 성공한 후에는 백엔드 api 서버는 access token이라고 하는 데이터를 프론트에게 전송한다. 그리고 프론트는 해당 사용자의 access token을 http request에 첨부해서 서버에 전송한다. http는 stateless이다. 각각의 http 통신은 독립적이기 때문에 이전에 어떠한 http 통신들이 오고가고 실행됐는지 알지못한다.여기서 문제가 발생하게 된다. 현재의 http에서 이전에 이미 인증이 진행됐는지 알지 못하기때문.그러모르 http 통신을 할 때는 해당 http 요청을 처리하기 위해서 필요한 모든 데이터를 첨부해서 요청을 보내야 한다. 로그인 정보 또한 http 요청에 첨부해서 보내야 api 서버에서 해당 사용자가 이미 로그인된 상태인 것을 알 수 있다. access token이 로그인 정보를 담고있다.api 서버에서 사용자의 로그인 정보를 access token의 형태로 생성하여 프론트 서버에 전송하면 프론트 서버는 백엔드 서버로부터 전송받은 access token을 그대로 다시 백엔드에 http 요청을 보낼때 첨부해서 보낸다. 그러면 백엔드 api에서는 프론트가 보내준 access token을 통해 해당 사용자의 로그인 여부를 알 수 있다.
access token을 생성하는 방법 중 내가 알고 있는것은 JWT(Json Web Tokens)뿐이다.. ㅠㅠㅠ 더 공부를 하긴해야함..
JSON 데이터를 token으로 변환하는 방식이다.
어떠한 사용자가 로그인할려고 이런 http요청을 backend api에 보낸다고 가정해보자.
post /auth HTTP/1.1
Content-Type : application/json
{
"name" : "mun0",
"password" : "1234"
}
백엔드 서버에서는 전송받은 사용자 id,pw을 확인하는 절차를 거친 후 인증이 되면 해당 사용자의 아이디를 json형태로 생성한다.
{
user_id = 1
}
{
"access_token" : "dfsojioajfisjdfklsajflksajfklsadf"
}
access token을 받은 프론트는 쿠키나 세션에 access token을 저장하고 있다가 해당 사용자를 위한 http 요청을 백엔드 서버 api에 보낼 때 access token을 첨부해서 보낸다. json 데이터를 토큰화 시키는 이유는 해킹 문제때문. json 데이터를 누구나 쉽게 http request에 전송할 수 있으므로 다른 유저를 해당 유저로 인식할 수 있다. jwt는 검증의 기능도 있다.
JWT - header / payload / signature
xxxxx.yyyyy.zzzzzz
header : token type(jwt) / hash algorithm
{
"alg" : "HS256",
"typ" : "JWT"
}
payload : jwt을 통해 실제로 서버 간에 전송하고자 하는 데이터부분
class SignUpView(View):
#회원가입은 post (왜냐? 유저가 유저 정보를 줘야하니까!)
def post(self, request):
data = json.loads(request.body)
email_validator = RegexValidator(regex = "^([a-zA-Z0-9_\-\.]+)@([a-zA-Z0-9_\-\.]+)\.([a-zA-Z]{2,5})$")
password_validator = RegexValidator(regex = "^[A-Za-z0-9!@#$%^&+=]{8,100}$")
phone_number_validator = RegexValidator(regex = "^[0-9]{9,14}$")
try:
email_validator(data['email'])
password_validator(data['password'])
phone_number_validator(data['phoneNumber'])
if User.objects.filter(email = data['email']).exists():
return JsonResponse({'message':'DUPLICATED_EMAIL'}, status=401)
password_crypt = bcrypt.hashpw(data['password'].encode('utf-8'), bcrypt.gensalt()).decode('utf-8')
sign_up_user = User.objects.create(
email = data['email'],
password = password_crypt,
phone_number = data['phoneNumber'],
name = data['name'],
)
token = jwt.encode({'id' : sign_up_user.id}, SECRETKEY['secret'], algorithm = ALGORITHM['algorithm'])
access_token = token.decode('utf-8')
return JsonResponse({'access_token':access_token}, status=200)
except ValidationError:
return JsonResponse({'message':'INVALID_EMAIL_OR_PASSWORD_OR_PHONE_NUMBER'}, status=400)
except KeyError:
return JsonResponse({'message':'KEY_ERROR'}, status=400)