API와 JWT

API


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들이 있어서 그걸 참고하면 좋다.


Api 참고링크 1
Api 참고링크 2


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에서 개발할 때는 서버와 클라이언트가 분리되어 있지 않아 바로 서버에서 불러와 쓸 수 있기 때문에 클라이언트단에 저장하지 않는다.


JWT 참고링크 3