今回は、Flask-Login Library と SQLAlchemy を使って Flask のユーザ認証をコーディングします。
現在、ほとんどのWebサイトにはユーザー認証の仕組みが組み込まれています。
ユーザー認証のアカウントは、直接設定することもできますし、Google や Facebook、Apple などのサードパーティを経由して設定することもできます。
Appleなど。
典型的なユーザーログインのページは、こんな感じです。
pip install flask-login
|
ユーザー認証は、特定のユーザーだけがアクセスできるようにユーザーデータを保護するため、ウェブページの重要な部分です。
ユーザーを認証するには、さまざまな方法があります。
-
- クッキーベースの認証
- トークンによる認証
- OAuth認証
- OpenId
- Saml
これからFlask-Login認証を使ってコーディングしていきます。
では、コーディングの部分に飛び込んでみましょう。
この記事もチェック:Flaskでフォームを実装|ユーザーの入力を受け取る方法などを解説
Flask ユーザー認証のハンズオン
Flask-login は、Cookie ベースの認証を使っています。
クライアントが認証情報を使ってログインすると、Flask はユーザー ID を含むセッションを作成し、そのセッション ID をクッキーでユーザーに送ります。
まず最初に、Flask-Loginをインストールする必要があります。
from werkzeug.security import generate_password_hash
a = generate_password_hash('1234')
print(a)
|
インストールが完了したら、いよいよコーディングに入ります。
この記事もチェック:FlaskでのMySQLの使い方|インストールや接続を解説する
1. models.py ファイルのコーディング
まず、ユーザの認証情報を保存するためのUser Modelを作成します。
このために、Flask_SQLAlchemyとSQLiteデータベースを使用します。
ここで注意すべき重要な点は、ユーザーのパスワードを直接データベースに保存できないことです。
なぜなら、もしハッカーがサイトにアクセスした場合、データベースからすべての情報を取得できてしまうからです。
というのも、もしハッカーがサイトにアクセスした場合、データベースからすべての情報を取得できてしまうからです。
しかし、Flask werkzeug にはこの問題に対処するための機能が組み込まれているので、心配はいりません。
この記事もチェック:FlaskのCookiesの設定、保存や取得等の使い方を分かりやすく解説する
1.1 パスワードハッシュを設定する
パスワードハッシュを使用することで解決します。
ハッシュとは何か、ターミナルでPythonシェルを開き、次のコマンドを実行します。
from werkzeug.security import generate_password_hash, check_password_hash
a = generate_password_hash('1234')
print(a)
chech_password_hash(a,'1234')
|
以下のような長いランダムな文字列が得られます。
pip install flask-sqlalchemy
|
したがって、ハッカーがアクセスしたとしても、復号化することはできません。
また、Hashとパスワードを比較するために、check_password_hashという関数も用意されています。
これは以下のように動作します。
from flask_sqlalchemy import SQLAlchemy
from werkzeug.security import generate_password_hash, check_password_hash
from flask_login import UserMixin
db = SQLAlchemy()
class UserModel(UserMixin, db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(80), unique=True)
username = db.Column(db.String(100))
password_hash = db.Column(db.String())
def set_password(self,password):
self.password_hash = generate_password_hash(password)
def check_password(self,password):
return check_password_hash(self.password_hash,password)
|
マッチしたらTrue、マッチしなかったらFalseを返します。
from flask_login import LoginManager
#...login = LoginManager()
#... |
1.2 データベースにハッシュ化されたパスワードを追加する
また、FlaskSQLAlchemy を持っていない場合は、pip コマンドでインストールするだけです。
from flask_login import LoginManager
login = LoginManager()
@login.user_loader
def load_user(id):
return UserModel.query.get(int(id))
|
SQLAlchemy がインストールできたら、models.py を作成して、以下のコードを追加してください。
from flask_sqlalchemy import SQLAlchemy
from flask_login import UserMixin
from werkzeug.security import generate_password_hash, check_password_hash
from flask_login import LoginManager
login = LoginManager()
db = SQLAlchemy()
class UserModel(UserMixin, db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(80), unique=True)
username = db.Column(db.String(100))
password_hash = db.Column(db.String())
def set_password(self,password):
self.password_hash = generate_password_hash(password)
def check_password(self,password):
return check_password_hash(self.password_hash,password)
@login.user_loader
def load_user(id):
return UserModel.query.get(int(id))
|
ここで
- email、username、password のハッシュを保存しています。
- また、パスワードハッシュを生成するためのset_passwordと、それらを比較するためのcheck_passwordという2つのクラスメソッドを定義します。
また、flaskのintek_loginライブラリのUserMixinを使用しています。
UserMixinは後で使用するいくつかの組み込み関数を持っています。
- isÂi_authenticated: ユーザーが有効なクレデンシャルを持っている場合にTrueを返す
- isanthus_active: ユーザーのアカウントがアクティブであればTrueを返す。インスタグラムで無効化されたアカウントはすべてFalseを返します。
- is_anonymous: 匿名。レギュラーユーザーにはFalseを、ファーストタイマー/匿名ユーザーにはTrueを返す。
- get_id(): ユーザーの一意な識別子を文字列で返す。
この記事もチェック:Flaskのライブラリflask-wtfの使い方|簡単にフォームを作成するよ
1.3. Flask_login エクステンションの設定
また、Flaskの拡張機能であるFlask_loginを作成し、初期化する必要があります。
コードで行います。
from flask import Flask
app =Flask(__name__)
app.run(host='localhost', port=5000)
|
先ほど説明したように、FlaskはログインしたユーザのUser IDをセッションに保存しています。
Flask_Loginはデータベースについて何も知らないので、両者をリンクさせる関数を作成する必要があります。
これはuser_loader関数を使用して行います。
構文は以下の通りです。
from flask import Flask
app =Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///<db_name>.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.run(host='localhost', port=5000)
|
この記事もチェック:Flaskの便利な拡張機能(Extension)を紹介していく
1.4. コードの完成
models.pyの部分は以上です。
一度、コード全体を見てみましょう。
from flask import Flask
from models import db
app =Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///<db_name>.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db.init_app(app)app.run(host='localhost', port=5000)
|
FlaskのSQLAlchemyに慣れていない方は、SQLAlchemyの記事もご覧ください。
2. Flask のメインアプリケーションファイルをコーディングする
では、メインの Flask アプリケーションファイルをコーディングしてみましょう。
from flask import Flask
from models import db
app =Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///<db_name>.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db.init_app(app)@app.before_first_request
def create_table():
db.create_all()
app.run(host='localhost', port=5000)
|
2.1 データベースと Flask ファイルをリンクする
さて、SQLALchemy と SQLite データベースをリンクする必要があります。
そのために、次のコードを追加します。
from flask import Flask
from models import login
app =Flask(__name__)
login.init_app(app)app.run(host='localhost', port=5000)
|
を好きな名前に置き換えるだけです。
また、SQLAlchemy の DB インスタンス(models.py にあります)を、メインアプリにリンクさせる必要があります。
そのために、以下を追加します。
from flask import Flask
from models import login
app =Flask(__name__)
login.init_app(app)login.login_view = 'login'
app.run(host='localhost', port=5000)
|
次に、最初のユーザリクエストの前に、データベースファイルを作成するコードを追加します。
これは以下のように行います。
from flask import Flask, render_template
from flask_login import login_required
@app.route('/blogs')
@login_requireddef blog():
return render_template('blog.html')
|
これでDBの部分はすべて終わりました。
では、Flask_loginの部分に移りましょう。
この記事もチェック:Flaskにおけるアプリケーションコンテキストとリクエストコンテキストを解説する
2.2 アプリにユーザー認証を追加する
DB インスタンスと同様に、ログインインスタンスもアプリにリンクする必要があります。
これを行うには、以下を使用します。
<h2>Welcome to the Blog</h2>
<h3>Hi {{ current_user.username }}</h3>
<a href="{{ url_for('logout')}}">Logout Here</a>
|
その後、Flask_loginに、未認証のユーザがリダイレクトされるページについて伝えます。
したがって、コードを追加します。
from flask import Flask, request, render_template
from flask_login import current_user, login_user
@app.route('/login', methods = ['POST', 'GET'])
def login():
if current_user.is_authenticated:
return redirect('/blogs')
if request.method == 'POST':
email = request.form['email']
user = UserModel.query.filter_by(email = email).first()
if user is not None and user.check_password(request.form['password']):
login_user(user)
return redirect('/blogs')
return render_template('login.html')
|
リダイレクトページを指定したら、認証が必要なすべてのウェブページビューに @login_required デコレーターを追加するだけでよいです。
かっこいい! これで、ログイン、登録、ログアウトのビューだけが残りました。
しかし、その前に、認証後にユーザーが見ることができるシンプルなページをコーディングしてみましょう。
2.3 シンプルなビューのコード化
それでは、簡単なビューを追加してみましょう。
<form action="" method = "POST">
<p>email <input type = "email" name = "email" /></p>
<p>password <input type = "password" name = "password" /></p>
<p> submit <input type = "submit" value = "Submit" /></p>
</form>
<h3>Dont Have an account??</h3>
<h3><a href = "{{url_for('register') }}">Register Here</a></h3>
|
ここでは @login_required デコレーターを使用していることに注意しましょう。
blog.html のテンプレートは以下のようになります。
from flask import Flask, request, render_template
from flask_login import current_user
@app.route('/register', methods=['POST', 'GET'])
def register():
if current_user.is_authenticated:
return redirect('/blogs')
if request.method == 'POST':
email = request.form['email']
username = request.form['username']
password = request.form['password']
if UserModel.query.filter_by(email=email):
return ('Email already Present')
user = UserModel(email=email, username=username)
user.set_password(password)
db.session.add(user)
db.session.commit()
return redirect('/login')
return render_template('register.html')
|
テンプレートについてもっと知りたい方は Flask Templates の記事を参照してください。
2.3 ログインビューのコード化
ログインビューはシンプルなものになります。
それは以下のことを行う必要があります。
- ユーザがすでに認証されている場合、blogページにリダイレクトするか、HTMLフォームを表示します。
- DBからユーザ情報を取得します。
- 認証情報を比較し、正しければ、blogs ページにリダイレクトする
というわけで、コードを追加します。
<form action="" method = "POST">
<p>email <input type = "email" name = "email" /></p>
<p>Username <input type = "text" name = "username" /></p>
<p>password <input type = "password" name = "password" /></p>
<p> submit <input type = "submit" value = "Submit" /></p>
</form>
<h3>Already Have an Account?</h3><br>
<h3><a href ="{{url_for('login')}}">Login Here</a></h3>
|
そして、login.html テンプレートを追加します。
from flask import Flask, render_template
from Flask_login import logout_user
@app.route('/logout')
def logout():
logout_user()
return redirect('/blogs')
|
2.4 レジスタビューのコード化
Register View は、以下の機能を備えている必要があります。
- ユーザーがすでに認証されている場合、blog ページにリダイレクトするか、HTML フォームを表示します。
- ユーザーのデータを DB に追加する
- ログインページへのリダイレクト
というわけで、コードは以下のようになります。
from flask import Flask,render_template,request,redirect
from flask_login import login_required, current_user, login_user, logout_user
from models import UserModel,db,login
app = Flask(__name__)
app.secret_key = 'xyz'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///data.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db.init_app(app)login.init_app(app)login.login_view = 'login'
@app.before_first_request
def create_all():
db.create_all()
@app.route('/blogs')
@login_requireddef blog():
return render_template('blog.html')
@app.route('/login', methods = ['POST', 'GET'])
def login():
if current_user.is_authenticated:
return redirect('/blogs')
if request.method == 'POST':
email = request.form['email']
user = UserModel.query.filter_by(email = email).first()
if user is not None and user.check_password(request.form['password']):
login_user(user)
return redirect('/blogs')
return render_template('login.html')
@app.route('/register', methods=['POST', 'GET'])
def register():
if current_user.is_authenticated:
return redirect('/blogs')
if request.method == 'POST':
email = request.form['email']
username = request.form['username']
password = request.form['password']
if UserModel.query.filter_by(email=email).first():
return ('Email already Present')
user = UserModel(email=email, username=username)
user.set_password(password)
db.session.add(user)
db.session.commit()
return redirect('/login')
return render_template('register.html')
@app.route('/logout')
def logout():
logout_user()
return redirect('/blogs')
|
したがって、register.htmlページは次のようになります。
python filename.py |
2.5 ログアウトビューのコード化
ログアウトビューは、単純にユーザーをログアウトさせるだけです。
そのため、以下のコードを追加します。
これで終わりです! それでは、このセクションの全コードを見てみましょう。

UserModel.query.filter_by(email=email).first() は、データベースから取得した最初のユーザーを返すか、ユーザーが見つからない場合は None を返します。
Flask ユーザー認証アプリケーションの実装
最後にアプリをテストしてみましょう。
Flaskのファイルを実行します。

そして、”/blogs “にアクセスしてみてください。
ログインページにリダイレクトされます。

registerをクリックし、あなたの情報を追加します。

送信をクリックすると、ログインページに戻ります。
今回は認証情報を入力し、ログインしてください。
ブログのページが表示されます。

注意: a@gmail.com のような簡単なメールを使用すると、Chromeブラウザで以下のようなエラーが発生する場合があります。

このように、”blogs” エンドポイントにリダイレクトされました。
上のスクリーンショットでは、非常に弱いパスワードでランダムな非実在を使用したため、セキュリティメッセージがポップアップしました。
より強力なパスワードと適切なメールアドレスを使用して同じことを試せば、この場合のようなセキュリティ警告ではなく、直接ブログのページが表示されるようになります。
この記事もチェック:Flaskのエラー処理|エラーメッセージの表示やエラーログを表示させる方法
まとめ
以上です。
以上、Flask のユーザー認証についてでした。
Flask のセッションとクッキーがどのように動作するかについては、 Flask のセッションとクッキーの記事を参照してください。
次回は、アプリケーションをクラウドサーバにデプロイします。