JWT를 이용한 로그인 기능

namu445 2022. 1. 16. 21:35

Jason Web Token

Jason 또는 평문을 서명(JWS)하거나 암호화(JWE) 한다. [RFC7519]

이번 프로젝트에서는 패스워드를 SHA256으로 암호화해서 DB에 저장하고, 서명이 들어간 토큰을 클라이언트에게 전달하기 위해서 JWT를 사용했습니다.

 

Flask에 JWT를 사용했습니다. PyJWT를 설치했습니다. 

 

로그인이 성공한 경우 저장된 쿠키(토큰)

 

 

//JWT import
import jwt

//JWT 서명 과정에서 키를 사용합니다. 키 값은 자유롭게 지정할 수 있습니다.
SECRET_KEY = 'SPARTA'


//로그인은 POST 방식으로 진행합니다.
<클라이언트>
$.ajax({
                type: "POST",
                url: "/sign_in",
                data: {
                    //username을  user_give로 전달
                    username_give: username,
                    //password를  password_give로 전달
                    password_give: password
                },
                success: function (response) {
                    //로그인이 성공했다면
                    if (response['result'] == 'success') {
                        //쿠키에 토큰값을 mytoken으로 저장
                        $.cookie('mytoken', response['token'], {path: '/'});
                        //쿠키 저장하고 메인 페이지로 이동
                        window.location.replace("/")
                    } else {
                        //성공하지 못하면 서버에서 받은 경고 메세지 출력
                        alert(response['msg'])
                    }
                }
            });



<서버>
@app.route('/sign_in', methods=['POST'])
def sign_in():
    # 클라이언트에서 보낸 아이디, 패스워드 저장
    username_receive = request.form['username_give']
    password_receive = request.form['password_give']

    # 저장한 패스워드를 SHA256으로 암호화해서 해쉬 값으로 저장 암호화한 패스워드는 복호화가 불가능한듯
    pw_hash = hashlib.sha256(password_receive.encode('utf-8')).hexdigest()
    # db에 아이디와 패스워드 해쉬 값 조회
    result = db.users.find_one({'username': username_receive, 'password': pw_hash})

    # 조회 결과가 none이 아니라면(조회결과가 있다는 이야기고 따라서 로그인이 가능하다)
    if result is not None:
        # payload에 아이디와 토큰 유효시간 저장
        payload = {
            'id': username_receive,
            'exp': datetime.utcnow() + timedelta(seconds=60 * 60 * 24)  # 로그인 24시간 유지
        }
        # AWS에서는 .decode('utf-8')이 있어야 에러나 나지 않습니다.
        # 패이로드를 키로 암호화해서 토큰에 저장, 이는 키로 복호화가 가능한 듯, encoding과 encryption은 다르며 JWT는 서명용이라는 글이 있다
        token = jwt.encode(payload, SECRET_KEY, algorithm='HS256').decode('utf-8')

        # 성공 결과와 토큰 정보를 클라이언트에 리턴 합니다.
        return jsonify({'result': 'success', 'token': token})
    # 찾지 못하면
    else:
        return jsonify({'result': 'fail', 'msg': '아이디/비밀번호가 일치하지 않습니다.'})
        
        
        
//페이지 이동 시 토큰 정보를 확인하는 예시        
@app.route('/')
def home():
	//클라이언트의 토큰(mytoken) 정보를 받습니다.
    token_receive = request.cookies.get('mytoken')
    try:
    	//토큰의 payload를 키로 decode 합니다.
        payload = jwt.decode(token_receive, SECRET_KEY, algorithms=['HS256'])
		
        //payload의 id를 user_id에 저장합니다.
        user_id = payload['id'];
		
        //DB에서 ID를 검색합니다.
        review_list = list(db.reviewlist.find({}, {'_id': False}));
		
        //이 예시는 인증에 성공한 경우 reviewList.html로 이동합니다.
        return render_template('reviewList.html', reviewList=review_list, userid=user_id)
        
    //유효시간이 만료된 경우    
    except jwt.ExpiredSignatureError:
        return redirect(url_for("login", msg="로그인 시간이 만료되었습니다."))
    //ID정보를 DB에서 찾지 못한경우
    except jwt.exceptions.DecodeError:
        return redirect(url_for("login", msg="로그인 정보가 존재하지 않습니다."))
        
        
        
//토큰의 유효기간이 남아있을 때 로그아웃
function logout() {
	//쿠키를 삭제합니다. 이 예시에서 쿠키는 mytoken이라는 이름으로 저장되어있습니다.
    $.removeCookie('mytoken');
    alert('로그아웃 완료!');
    window.location.href = '/login';
}

 

 

RFC7519: https://datatracker.ietf.org/doc/html/rfc7519

 

rfc7519

 

datatracker.ietf.org

 

'' 카테고리의 다른 글

HTTP  (0) 2022.01.21
API?  (0) 2022.01.16
Bulma  (0) 2022.01.13
쿠키, 세션  (0) 2021.10.21
Sublime text sudo권한으로 실해하기  (0) 2021.10.17