設定方法
LoginManager の設定
LoginManagerは、ユーザーのログイン状態を管理してくれる
from flask_login import LoginManager
#インスタンス化
login_manager = LoginManager()
#アプリをログイン機能を紐付ける
login_manager.init_app(app)
#未ログインユーザーを転送する(ここでは'login'ビュー関数を指定)
login_manager.login_view = 'login'
#現在のログインユーザーの情報を保持し、必要なときに参照できるようになる。
@login_manager.user_loader
def load_user(user_id):
return User.query.get(user_id)
モデルの設定
UserMixinでログインに必要な機能を継承する。
werkzeugでパスワードハッシュ化とチェック機能を追加。
#UserMixin、ログイン・ログアウトで必要なライブラリをインポート
from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user
#パスワードをハッシュ化するライブラリをインポート
from werkzeug.security import check_password_hash, generate_password_hash
#UserMixinをを継承
class User(db.Model, UserMixin):
__tablename__ = 'user'
id = db.Column(db.Integer, primary_key = True)
email = db.Column(db.String(64), unique=True, index=True)
username = db.Column(db.String(64), unique=True, index=True)
password_hash = db.Column(db.String(128))
#パスワードチェックする関数を追記
def check_password(self, password):
return check_password_hash(self.password_hash, password)
ログイン用フォーム
class LoginForm(FlaskForm):
email = StringField('Email', validators=[DataRequired(), Email(message='正しいメールアドレスを入力してください')])
password = PasswordField('Password', validators=[DataRequired()])
submit = SubmitField('ログイン')
ビュー関数
@app.route('/login', methods=['GET', 'POST'])
def login():
form = LoginForm()
if form.validate_on_submit():
#フォーム入力したアドレスがDB内にあるか検索
user = User.query.filter_by(email=form.email.data).first()
if user is not None:
#check_passwordはUserモデル内の関数
if user.check_password(form.password.data):
#ログイン処理。ログイン状態として扱われる。
login_user(user)
next = request.args.get('next')
if next == None or not next[0] == '/':
next = url_for('user_maintenance')
return redirect(next)
else:
flash('パスワードが一致しません')
else:
flash('入力されたユーザーは存在しません')
return render_template('login.html', form=form)
未ログイン状態でログインが必要なページを訪れた場合は、クエリー文字列のあとログイン後に遷移するページとしてそのページのURLが表示される。
例) "/register"がログイン必須のページだった場合、以下のURLが表示される。
/login?next=%2Fregister
"%2F"は、"/"(スラッシュ)の意味
この場合、ログイン成功したら、"/register"に遷移する。
そのため、クエリー文字列に"next"がない、もしくはnextの1文字目が"/"ではない場合は、
ユーザー一覧画面へ遷移する。
"next"がない = 「通常どおり、ログイン状態でアクセスしようとした」 ので、ログイン後に本来リダイレクトすべきページに遷移する。
"next"がある = 「未ログインでログイン必須のページにアクセスしようとした」ので、そのユーザーがアクセスしようとしたページにリダイレクトさせる。
上記の条件分岐は以下のようなコードとなる。
next = request.args.get('next')
if next == None or not next[0] == '/':
next = url_for('user_maintenance')
return redirect(next)
ログインページのテンプレート
<h2>ログインページ</h2>
<div>
<a href="{{ url_for('register' )}}">
新規登録へ
</a>
</div>
<form method="POST">
<!-- CSRF対策(トークンが生成される) -->
{{ form.hidden_tag() }}
<div>
{{ form.email.label }} {{ form.email() }}
{% for error in form.email.errors %}
<span style="color: red;">{{ error }}</span>
{% endfor %}
</div>
<div>
{{ form.password.label }} {{ form.password() }}
{% for error in form.password.errors %}
<span style="color: red;">{{ error }}</span>
{% endfor %}
</div>
{{ form.submit() }}
</form>
ログインユーザーを表示
is_authenticated はログインしているかどうかを判断する関数。
モデルにUserMixinを継承することで使えるようになる。
{% if current_user.is_authenticated %}
<a class="nav-link" href="{{ url_for('logout') }}">ログアウト</a>
{{ current_user.username }}
{% endif %}
新規ユーザー登録時に、パスワードのハッシュ化
Python Property・・・クラスのインスタンスに保持するデータで、値の参照や変更方法を制限することが可能
#値の参照
@property
def password(self):
raise AttributeError('password is not a readable attribute')
#値の設定
@password.setter
def password(self, password):
self.password_hash = generate_password_hash(password)
モデルクラスの初期設定の関数も修正。
"password_hash"から"hash"を取り除き、passwordはハッシュ化されていない状態で渡されることを明示する。
setterでパスワードを受け取ってハッシュ化して、その値を"password_hash"に入れられデータベースに登録されるという流れ。
def __init__(self, email, username, password):
self.email = email
self.username = username
self.password = password
新規登録のビュー関数"register"も修正。
モデルクラスをインスタンス化する際の引数"password_hash"から"hash"を取り除く。
@app.route("/register", methods=["GET", "POST"])
def register():
form = RegistrationForm()
if form.validate_on_submit():
user = User(email=form.email.data, username=form.username.data, password=form.password.data)
db.session.add(user)
db.session.commit()
ログアウト機能
@app.route('/logout')
def logout():
#現在のユーザーをログアウト状態にする
logout_user()
return redirect(url_for('login'))
ログインユーザーのみアクセス可能にする
login_required をインポート
from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required
@login_required を付けることで、未ログインユーザーはログインページに転送されるようになる。
@app.route('/logout')
@login_required
def logout():
logout_user()
return redirect(url_for('login'))
以下のコードでリダイレクトするビュー関数と、エラーメッセージを定義する
#ビュー関数"login"にリダイレクトするよう定義
login_manager.login_view = 'login'
# 未ログインユーザーにメッセージ表示
def localize_callback(*args, **kwarg):
return 'このページにアクセスするには、ログインが必要です'
login_manager.localize_callback = localize_callback