로그인과 로그아웃을 구현해보도록 하겠습니다.
from flask_wtf import FlaskForm
from wtforms import StringField, TextAreaField, PasswordField
from wtforms.fields.html5 import EmailField
from wtforms.validators import DataRequired, Email, EqualTo, Length
class QuestionForm(FlaskForm):
subject = StringField('제목', validators=[DataRequired('제목은 필수 입력 항목입니다.')])
content = TextAreaField('내용', validators=[DataRequired('내용은 필수 입력 항목입니다.')])
class AnswerForm(FlaskForm):
content = TextAreaField('내용', validators=[DataRequired('내용은 필수 입력 항목입니다.')])
class UserCreateForm(FlaskForm):
username = StringField('사용자이름', validators=[
DataRequired(), Length(min=3, max=25)])
password1 = PasswordField('비밀번호', validators=[
DataRequired(), EqualTo('password2', '비밀번호가 일치하지 않습니다.')])
password2 = PasswordField('비밀번호확인', validators=[DataRequired()])
email = EmailField('이메일', [DataRequired(), Email()])
class UserLoginForm(FlaskForm):
username = StringField('사용자이름', validators=[DataRequired(), Length(min=3, max=25)])
password = PasswordField('비밀번호', validators=[DataRequired()])
forms.py 입니다.
로그인할 때 검증에 필요한 userLoginForm을 만들었습니다. (이미 했던 거라 설명은 생략하겠습니다.)
from flask import Blueprint, request, render_template, flash, url_for, session
from werkzeug.security import generate_password_hash, check_password_hash
from werkzeug.utils import redirect
from .. import db
from pybo.forms import UserCreateForm, UserLoginForm
from pybo.models import User
bp = Blueprint('auth', __name__, url_prefix='/auth')
@bp.route('/signup/', methods=('GET','POST'))
def signup():
form = UserCreateForm()
if request.method == 'POST' and form.validate_on_submit():
user = User.query.filter_by(username=form.username.data).first()
if not user:
user = User(username=form.username.data,
password=generate_password_hash(form.password1.data),
email=form.email.data)
db.session.add(user)
db.session.commit()
return redirect(url_for('main.index'))
else:
flash('이미 존재하는 사용자입니다.')
return render_template('auth/signup.html', form=form)
@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)
auth_views.py 입니다.
POST방식이라면 로그인을 수행하고 그렇지 않으면 GET방식으로 auth/login.html 템플릿을 렌더링합니다.
POST 방식으로 들어오고 올바른 폼이라면 error에 None을 넣습니다.
username.data값과 DB에 있는 username이 일치하는 경우 비밀번호를 체크합니다.
물론 없는 경우(if not user) '존재하지 않는 사용자입니다'를 넣습니다.
비밀번호는 암호화되어서 저장했으므로 바로 비교할 수 없습니다.
입력된 비밀번호는 반드시 check_pass_word_hash 함수로 똑같이 암호화하여 비교해야합니다.
틀린 경우는 '비밀번호가 올바르지 않습니다.'를 넣습니다.
만약 error값이 그대로 None이면 (다 일치했으니까 그대로이다.) session에 키와 키값을 저장합니다
키에는 user_id라는 문자열을 넣고 키값은 DB에서 조회된 사용자 id값을 넣습니다.
그 후 main.index로 리다이렉트합니다.
세션은 플라스크 서버를 구하는 동안 영구히 참조할 수 있는 값입니다.
세션은 시간제한이 있어서 일정 시간 접속하지 않으면 자동으로 삭제됩니다.(?)
[뭔 소린지 모르겠습니다. 책에 있는 내용이라...]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{% 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 %}
</body>
</html>
templates/auth/login.html 입니다.
로그인 템플릿을 만들어주세요
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container-fluid">
<a class="navbar-brand" href="{{ url_for('main.index') }}">Pybo</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<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>
</div>
</div>
</nav>
</body>
</html>
navbar.html 입니다.
네비게이션 바에 로그인 링크를 또한 추가하겠습니다.
로그인 여부는 session에 저장된 값을 조사하면 할 수 있습니다.
단순히 session에 저장된 user_id값 여부로 로그인을 확인할 수 있지만
여기서는 좀 더 널리 사용할 수 있는 방법을 선택하겠습니다.
from flask import Blueprint, request, render_template, flash, url_for, session, g
from werkzeug.security import generate_password_hash, check_password_hash
from werkzeug.utils import redirect
from .. import db
from pybo.forms import UserCreateForm, UserLoginForm
from pybo.models import User
bp = Blueprint('auth', __name__, url_prefix='/auth')
@bp.route('/signup/', methods=('GET','POST'))
def signup():
form = UserCreateForm()
if request.method == 'POST' and form.validate_on_submit():
user = User.query.filter_by(username=form.username.data).first()
if not user:
user = User(username=form.username.data,
password=generate_password_hash(form.password1.data),
email=form.email.data)
db.session.add(user)
db.session.commit()
return redirect(url_for('main.index'))
else:
flash('이미 존재하는 사용자입니다.')
return render_template('auth/signup.html', form=form)
@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)
@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)
auth_view.py 입니다.
load_logged_in_user 함수를 구현하겠습니다.
@bp.before_app_request 어노테이션을 사용하겠습니다.
이 어노테이션은 라우트 함수보다 먼저 실행 됩니다. 즉 모든 라우트 함수보다 먼저 실행이 되는 것이죠
g는 플라스크가 제공하는 변수입니다.(누군 글로벌변수, 누구는 로컬변수라고하네요...)
JSP에서 Application 영역입니다. 즉 서버가 꺼질 때까지 남아있다는 말입니다.
일단 로그인을 했을때 session에 user_id를 저장했기 때문에 session에 user_id를 가져옵니다.
그게 있으면 이제 g.user에 user_id값을 쿼리문으로 조회해서 넣습니다.
(그냥 user_id를 바로 넣어도 되는 거 아닌가? 잘 모르겠네요..)
from flask import Blueprint, request, render_template, flash, url_for, session, g
from werkzeug.security import generate_password_hash, check_password_hash
from werkzeug.utils import redirect
from .. import db
from pybo.forms import UserCreateForm, UserLoginForm
from pybo.models import User
bp = Blueprint('auth', __name__, url_prefix='/auth')
@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.route('/signup/', methods=('GET','POST'))
def signup():
form = UserCreateForm()
if request.method == 'POST' and form.validate_on_submit():
user = User.query.filter_by(username=form.username.data).first()
if not user:
user = User(username=form.username.data,
password=generate_password_hash(form.password1.data),
email=form.email.data)
db.session.add(user)
db.session.commit()
return redirect(url_for('main.index'))
else:
flash('이미 존재하는 사용자입니다.')
return render_template('auth/signup.html', form=form)
@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)
@bp.route('/logout/')
def logout():
session.clear()
return redirect(url_for('main.index'))
auth_views.py 입니다.
logout 라우트를 만들어줬습니다.
user_id = session.get('user_id') 이거를 통해서 session에 값을 가져와서 g.user에 저장하는 형식이였기 때문에
session이 비게 되면 이제 더이상 로그인상태를 유지할 수 없습니다.
그리고 main.index로 리다이렉트 했습니다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container-fluid">
<a class="navbar-brand" href="{{ url_for('main.index') }}">Pybo</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
{% if g.user %}
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="{{ url_for('auth.logout') }}">{{ g.user.username }} (로그아웃)</a>
</li>
</ul>
{% else %}
<div class="collapse navbar-collapse" id="navbar-nav">
<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>
</div>
{% endif %}
</div>
</nav>
</body>
</html>
navbar.html 입니다.
if문을 이용해 g.user에 값이 들어가면 로그아웃이 나오게 하고 아니면 계정생성과 로그인이 나오게 했습니다.
'플라스크 (추후 수정)' 카테고리의 다른 글
Flask 게시물, 댓글 수정 및 삭제 추가하기 populate_obj, data-uri (0) | 2021.08.25 |
---|---|
Flask 글, 댓글 작성자 표시해보기 DB 테이블 수정과 DB접근 (0) | 2021.08.20 |
Flask 회원가입기능 추가 generate_password_hash, query.filter_by (0) | 2021.08.15 |
Flask 질문에 달린 답변 개수 표시하기 (0) | 2021.08.14 |
Flask 게시물 번호 바꾸기 (0) | 2021.08.14 |