SQLAlchemyとFlask-HTTPAuthを使って、Basic認証を実装する

入力されたユーザー名とパスワードが、事前にデータベースに登録されているユーザー名とハッシュ化されたパスワードと一致すれば、ページに入れるようにします。

Basic認証の使いどころがよくわかっていませんが、いつか使う日が来るかもしれないので、備忘録として残しておきます。

Basic認証(ベーシックにんしょう、Basic Authentication)とは、HTTPで定義される認証方式(HTTP認証)の一つ。基本認証と呼ばれることも。

Basic認証では、ユーザ名とパスワードの組みをコロン “:” でつなぎ、Base64でエンコードして送信する。このため、盗聴や改竄が簡単であるという欠点を持つが、ほぼ全てのWebサーバおよびブラウザで対応しているため、広く使われている。

盗聴や改竄を防ぐため、後にDigest認証というユーザ名とパスワードをMD5でハッシュ化して送る方法が考えられた。

引用:https://ja.wikipedia.org/wiki/Basic認証

こんな感じで、ページにアクセスするとユーザー名とパスワードを求められるやつですね。SSLと併用することが前提の認証方法みたいです。

Flask-HTTPAuthでは、Digest認証も使えます。

参考にしたサイトはこちら → https://pypi.org/project/Flask-HTTPAuth/

インストール

pip install Flask-HTTPAuth
pip install SQLAlchemy

ファイルの階層

.
├── app.py
├── db.py
├── db.sqlite
└── testuser.py

データベース

db.py

SQLAlchemyでusernameとpasswordを管理する簡単なデータベースを作成します。

from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.sql.schema import Column
from sqlalchemy.sql.sqltypes import Integer, String
from sqlalchemy.orm import scoped_session, sessionmaker

engine = create_engine('sqlite:///db.sqlite')
Base = declarative_base()

class User(Base):
    __tablename__ = 'user'
    id = Column(Integer, primary_key=True)
    username = Column(String, unique=True)
    password = Column(String, nullable=False)

Base.metadata.create_all(engine)
session_db = scoped_session(sessionmaker(
    autocommit=False,
    autoflush=False,
    bind=engine
))

Base.query = session_db.query_property()

ここにテストデータを1件だけ登録しておきます。

testuser.py

from werkzeug.security import generate_password_hash
from db import User, session_db

testuser = User(username = 'dattesar', password = generate_password_hash('dpass'))

session_db.add(testuser)
session_db.commit()
session_db.close()

Basic認証

app.py

from flask import Flask
from flask_httpauth import HTTPBasicAuth
from werkzeug.security import check_password_hash
from db import User

app = Flask(__name__)
auth = HTTPBasicAuth()

@auth.verify_password
def verify_pw(username,password):
    user = User.query.filter(User.username == username).one_or_none()
    if user and check_password_hash(user.password, password):
        return user

@app.route('/')
@auth.login_required
def index():
    return 'Hello, %s!' % auth.current_user().username

if __name__ == '__main__':
    app.run()

認証時に入力されたusernameをそのまま返す場合は、

@auth.verify_password
def verify_pw(username,password):
    user = User.query.filter(User.username == username).one_or_none()
    if user and check_password_hash(user.password, password):
        return username     # usernameに変更 このreturnがauth.current_user()に

@app.route('/')
@auth.login_required
def index():
    return 'Hello, %s!' % auth.current_user()      # auth.current_user()に変更