今回は、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_required def 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_required def 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 のセッションとクッキーの記事を参照してください。
次回は、アプリケーションをクラウドサーバにデプロイします。