API와 JWT
26 Jun 2022API
Flask -> jinja2 사용하기
이전에는 html(클라이언트단)에서 ajax로 get타입을 받아와서
let word = '{{ word }}'
$.ajax({
type: "GET",
url: `apiUrl/${word}`,
beforeSend: function (xhr) {
xhr.setRequestHeader("Authorization", "Token [내토큰]");
},
data: {},
error: function (xhr, status, error) {
alert("에러 발생!");
},
success: function (response) {
console.log(response)
}
});
-> api 요청보내기
$("selector").text(response["변수"]);
javascript로 html에 출력했다면
flask에선 app.py(서버단)에서 requests로 get을 받아와서
r = requests.get("apiUrl/{keyword}", headers={"Authorization": "Token [내토큰]"})
result = r.json()
print(result)
-> flask에서 요청보내기(app.py)
<div>{{ result.변수 }}</div>
-> api에서 보내지는 변수(html)
html에 렌더링을 변수로 보내고 jinja2로 직접 html에 받아온 변수를 넣어 출력
return render_template("보낼html",html에서받는변수=보내는변수)
{% set 변수 = 받은변수[index].하위속성 %}
{% if 변수 > 값 %}
{% for 단일변수 in 복수변수 %}
{{ 단일변수.하위속성 }}
{% endfor %}
{% endif %}
파라미터 값 가져오기
hostname?param=value
@app.route('/페이지')
def 변수():
서버에서받은변수 = requsts.args.get("param")
return render_template("페이지", 보낼변수=서버에서받은변수)
or
@app.route('/페이지/<param>')
def 변수(param):
return render_template("페이지", 보낼변수=param)
회원가입 API
post타입으로 보내면 requests로 받아서 저장하는데 password의 경우 암호화를 위해 해시함수를 이용한다.
해시함수란?
알고리즘의 한 종류로서 임의의 데이터를 입력 받아 항상 고정된 길이의 임의의 값으로 변환해주는 함수
- 동일한 입력값은 항상 같은 결과값이 나옴
- 입력값이 조금이라도 달라지면 완전히 다른 값이 나옴
- 결과값을 통해 입력값을 알아내는 것이 불가능
-> 강의에서 사용해본 SHA256은 항상 256바이트의 결과값이 나온다.
pw_hash = hashlib.sha256((패스워드request변수).encode('utf-8')).hexdigest()
result = db.user.insert_one({...,'pw':pw_hash,...})
return jsonify({'result':'success'})//성공하면 success라는 문구가 result로 보내짐
로그인 때도 유저가 입력한 값을 hash함수를 이용해서 암호화하고 암호화된 패스워드로 find_one을 한다.
기본적으로 API는 open api를 제공하는 곳에 상세한 document들이 있어서 그걸 참고하면 좋다.
JWT
유저가 로그인을 하면 그 로그인상태를 유지시켜줘야 하는데 기본적으로 cookie, session 그리고 JWT 방식이 있다.
session 방식은 어설프게나마 해본 적이 있는데 JWT는 처음이었다.
로그인을 할 때 유저가 입력한 input값이 db와 일치하는지 체크하고
result = db.user.find_one({'id',id_receive, 'pw':pw_hash})
찾으면 JWT 토큰을 만들어 발급한다.
if result is not None:
//if(result != null)같은...
payload = {
'id': id_receive,
'exp': datetime.datetime.utcnow() + datetime.timedelta(seconds=5)//5초 뒤에 만료
}
token = jwt.encode(payload, SECRET_KEY, algorithm='HS256').decode('utf-8')
return jsonify({'result':'success','token':token})
else:
return jsonify({...실패처리...})
JWT 토큰에는 payload와 시크릿키가 필요하다.
시크릿키가 있어야 토큰을 디코딩해서 payload 값을 볼 수 있기 때문.
즉, JWT 토큰을 풀면 유저ID값을 알 수 있기 때문에 exp에 만료시간을 넣었다.
만료시간이 지나면 시크릿키로 토큰을 풀 때 만료되었다고 에러가 난다.
유저 정보를 찾을 때는(유저 id나 닉네임을 html에서 호출해야 할 필요가 있을 때)
token_receibe = request.cookies.get('mytoken')
유효한 토큰 확인하고(만료되지 않은) try except를 써서 로그인 할 때 encode를 하는 것과는 반대로 decode를 해서 payload를 출력하고 그 payload id로 db에서 유저 정보를 찾고 return json으로 보내주면 된다.
만약 토큰이 만료되었으면
except jwt.ExpiredSignatureError:
return jsonify({만료되었다는 메시지 호출});
db에 유저 정보가 없으면(디코딩 에러)
except jwt.exceptions.DecodeError:
return jsonify({정보가 없다는 메시지 호출})
JWT가 알고 있던 방식(클라이언트 헤더에 저장했다가 불러오는 방식)과 강의에서 본 방식(서버에서 바로 불러오는 방식)이 다른 이유
(이대호 기술매니저님의 답변)
알고 있던 방식은 서버와 클라이언트가 분리되어 작업하기 때문에 서버단에서 토큰을 바로 가져오기 어려워서 그 방식을 취했던 것.
하지만 flask에서 개발할 때는 서버와 클라이언트가 분리되어 있지 않아 바로 서버에서 불러와 쓸 수 있기 때문에 클라이언트단에 저장하지 않는다.