로그인, 로그아웃 구현

jurin·2020년 12월 25일
0

플라스크 - python

목록 보기
12/17

책 '점프 투 플라스크'를 공부하면서 정리한 내용입니다.
출처 : https://wikidocs.net/book/4542

로그인 구현

1. 로그인 폼

forms.py에 로그인 폼 만들기

# --------------------------------- [edit] ---------------------------------- #
class UserLoginForm(FlaskForm):
    username = StringField('사용자이름', validators=[DataRequired(), Length(min=3, max=25)])
    password = PasswordField('비밀번호', validators=[DataRequired()])
# --------------------------------------------------------------------------- #

2. 로그인 라우트 함수 만들기 - 로그인 검증 과정 구현

auth_view.py에 로그인을 수행할 라우트 함수 작성

# --------------------------------- [edit] ---------------------------------- #
from flask import Blueprint, url_for, render_template, flash, request, session
from werkzeug.security import generate_password_hash, check_password_hash
# --------------------------------------------------------------------------- #
from werkzeug.utils import redirect

from pybo import db
# --------------------------------- [edit] ---------------------------------- #
from pybo.forms import UserCreateForm, UserLoginForm
# --------------------------------------------------------------------------- #

(... 생략 ...)

# --------------------------------- [edit] ---------------------------------- #
@bp.route('/login/', methods=('GET', 'POST'))
def login():
    form = UserLoginForm()
    if request.method == 'POST' and form.validate_on_submit():
        error = None
        user = User.query.filter_by(username=form.username.data).first()
        if not user:
            error = "존재하지 않는 사용자입니다."
        elif not check_password_hash(user.password, form.password.data):
            error = "비밀번호가 올바르지 않습니다."
        if error is None:
            session.clear()
            session['user_id'] = user.id
            return redirect(url_for('main.index'))
        flash(error)
    return render_template('auth/login.html', form=form)
# --------------------------------------------------------------------------- #

POST 방식 요청에는 로그인을 수행하고, GET 방식 요청에는 로그인 템플릿을 렌더링한다.

POST 방식 요청으로 로그인 작업 수행 과정
1. 폼 입력으로 받은 username으로 DB에 해당 사용자 있는지 검사
2. 만약 없으면 '존재하지 않는 사용자입니다.' 오류 발생
3. 존재한다면 폼 입력으로 받은 password와 check_password_hash 함수를 사용하여 DB의 비밀번호와 일치하는지 비교. DB에 저장된 비밀번호는 암호화되었으므로 입력된 비밀번호도 반드시 check_password_hash 함수로 똑같이 암호화하여 비교해야 한다.
4. 사용자도 존재하고 비밀번호도 올바르면 플라스크 세션(session)에 키와 키값을 저장. 키에는 'user_id'라는 문자열 저장, 키값은 DB에서 조회된 사용자의 id값 저장

  • 세션(session)
    • request와 마찬가지로 플라스크가 자동으로 생성하여 제공하는 변수
    • 플라스크 서버를 구동하는 동안에 영구히 참조할 수 있는 값
    • 다양한 URL 요청에 session값 사용 가능 ex) 현재 웹 브라우저를 요청한 주체가 로그인한 사용자인지 판별 가능.
  • request vs session
    request는 요청, 응답 과정만 사용할 수 있는 반면, session은 플라스크 서버를 구동하는 동안 영구히 사용할 수 있는 값이므로 사용자 id를 저장하거나 활용하는데 적합. 단, 시간제한이 있어서 일정 시간 접속하지 않으면 자동 삭제
  • 웹 브라우저와 서버의 실행 방식과 쿠키, 세션의 이해
    웹 프로그램은 [웹 브라우저 요청 → 서버 응답] 순서로 실행되며, 서버 응답이 완료되면 웹 브라우저와 서버 사이의 연결은 끊어짐.
    이 때 요청한 것 중 같은 브라우저인지 아닌지를 구분하게 해주는 것이 쿠키(Cookie).
  • 쿠키(Cookie)란? 쿠키는 웹 브라우저를 구별하는 값
  1. 웹 브라우저는 서버에서 받은 쿠키를 저장
  2. 이후 서버에 다시 요청시 이 쿠키 전송
  3. 서버는 웹 브라우저가 보낸 쿠키를 보고 이전에 보냈던 쿠키와 비교
    이 때 세션은 바로 쿠키 1개당 생성되는 서버의 메모리 공간이라고 할 수 있다.

3. 로그인 템플릿 만들기

auth/login.html 파일을 만들어서 로그인 폼에서 생성한 필드 2개(username, password)를 input 엘리먼트로 생성

{% extends "base.html" %}
{% block content %}
<div class="container my-3">
    <form method="post" class="post-form">
        {{ form.csrf_token }}
        {% include "form_errors.html" %}
        <div class="form-group">
            <label for="username">사용자 이름</label>
            <input type="text" class="form-control" name="username" id="username"
                   value="{{ form.username.data or '' }}">
        </div>
        <div class="form-group">
            <label for="password">비밀번호</label>
            <input type="password" class="form-control" name="password" id="password"
                   value="{{ form.password.data or '' }}">
        </div>
        <button type="submit" class="btn btn-primary">로그인</button>
    </form>
</div>
{% endblock %}

템플릿에서 <로그인> 버튼을 누르면 form 엘리먼트가 현재 웹 브라우저의 주소 창에 표시된 URL인 /auth/login/을 통해 POST 방식으로 요청된다.

4. 내비게이션 바에 로그인 링크 추가

<li class="nav-item ">
<!-- ------------------------------ [edit] -------------------------------- -->
                <a class="nav-link" href="{{ url_for('auth.login') }}">로그인</a>
<!-- ---------------------------------------------------------------------- -->
            </li>

로그인을 수행한 후에도 여전히 내비게이션 바에 여전히 '로그인'링크가 남아 있는데 '로그아웃'링크로 바뀌어야 한다. session에 저장된 값을 조사해서 사용자의 로그인 여부를 파악한다.

5. 로그인한 사용자 정보를 조회하는 함수 구현

auth_views.py파일에 load_logged_in_user 함수 구현

# --------------------------------- [edit] ---------------------------------- #
from flask import Blueprint, url_for, render_template, flash, request, session, g
# --------------------------------------------------------------------------- #
from werkzeug.security import generate_password_hash, check_password_hash
from werkzeug.utils import redirect

(... 생략 ...)

# --------------------------------- [edit] ---------------------------------- #
@bp.before_app_request
def load_logged_in_user():
    user_id = session.get('user_id')
    if user_id is None:
        g.user = None
    else:
        g.user = User.query.get(user_id)
# --------------------------------------------------------------------------- #

@bp.before_app_request 어노테이션을 사용. 이 어노테이션이 적용된 함수는 라우트 함수보다 먼저 실행된다. -> load_logged_in_user 함수는 모든 라우트 함수보다 먼저 실행

g : 플라스크가 제공하는 컨텍스트 변수로 request 변수와 마찬가지로 [요청 → 응답] 과정에서 유효하다.

이후 사용자 로그인 검사시 session을 조사할 필요가 없이 g.user에 값이 있는지만 알아내면 된다. g.user에는 User 객체가 있으므로 username, email 등 추가 정보 가능.

6. 내비게이션 바 수정

<div class="collapse navbar-collapse flex-grow-0" id="navbarNav">
<!-- ------------------------------ [edit] -------------------------------- -->
        {% if g.user %}
        <ul class="navbar-nav">
            <li class="nav-item ">
                <a class="nav-link" href="#">{{ g.user.username }} (로그아웃)</a>
            </li>
        </ul>
        {% else %}
<!-- ---------------------------------------------------------------------- -->
        <ul class="navbar-nav">
            <li class="nav-item ">
                <a class="nav-link" href="{{ url_for('auth.signup') }}">계정생성</a>
            </li>
            <li class="nav-item ">
                <a class="nav-link" href="{{ url_for('auth.login') }}">로그인</a>
            </li>
        </ul>
<!-- ------------------------------ [edit] -------------------------------- -->
        {% endif %}
<!-- ---------------------------------------------------------------------- -->
    </div>

g.user는 load_logged_in_user 함수로 생성한 정보값으로 로그인되어 있다면 g.user가 만들어진 상태니까 username 값과 '로그아웃'링크를 보여줄 것이다.

로그아웃 구현

1. 로그아웃 함수

auth_views.py에 /logout/ 라우트 URL에 매핑되는 logout 함수를 작성

@bp.route('/logout/')
def logout():
    session.clear()
    return redirect(url_for('main.index'))

session.clear()을 추가하여 세션에 저장된 user_id등 모든 값 삭제. -> g.user == None

2. 내비게이션 바 수정

'로그아웃' 링크 활성화

<li class="nav-item ">
<!-- ------------------------------ [edit] -------------------------------- -->
                <a class="nav-link" href="{{ url_for('auth.logout') }}">{{ g.user.username }} (로그아웃)</a>
<!-- ---------------------------------------------------------------------- -->
            </li>
profile
anaooauc1236@naver.com

0개의 댓글