FlaskでBasic認証

タイトル通りな感じで試したのでメモっておく。というかFlaskのドキュメントに載ってた。Flaskアプリ側でBasic認証を掛けたいという欲求

HTTP Basic Auth | Flask (A Python Microframework)

1. 準備

適当に2ページ分の出力をするFlaskアプリがあるとする。

from flask import Flask
app = Flask(__name__)

@app.route("/")
@requires_auth
def index():
    return "Hello Index!"

@app.route("/hello")
def hello():
    return "Hello World!"

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

これを main.py とかいうファイル名だとする。

2. Basic認証用のデコレータを用意する

HTTP Basic Auth | Flask (A Python Microframework)

from functools import wraps
from flask import request, Response

def check_auth(username, password):
    """This function is called to check if a username /
    password combination is valid.
    """
    return username == 'admin' and password == 'secret'

def authenticate():
    """Sends a 401 response that enables basic auth"""
    return Response(
    'Could not verify your access level for that URL.\n'
    'You have to login with proper credentials', 401,
    {'WWW-Authenticate': 'Basic realm="Login Required"'})

def requires_auth(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        auth = request.authorization
        if not auth or not check_auth(auth.username, auth.password):
            return authenticate()
        return f(*args, **kwargs)
    return decorated

ここのコードをもってきて、「decorator.py」とかいうファイル名にする。

ここまででこんなファイル構成構成になってる

/decorator.py
/main.py

3. デコレータ使ってみる

作ったデコレータを使ってみる。

from flask import Flask
from decorator import requires_auth # <- 追加 
app = Flask(__name__)

@app.route("/")
@requires_auth # <- 追加
def index():
    return "Hello Index!"

@app.route("/hello")
def hello():
    return "Hello World!"

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


これで 「http://127.0.0.1/」にアクセスすると、ID/PASSが求められるので、「admin/secret」て入力すると認証が通る。

4. サイト全体でBasic認証をかける

上のまんまだと、「http://127.0.0.1/hello」にはデコレータが無いのでBasic認証かからない。hello のviewにもデコレータを設定すればよいが、viewがいっぱいあると、面倒、

Flaskは、全てのviewアクションに対して、前処理を入れられるフックポイントが用意してあるのでそれを利用してサイト全体にBasic認証をかける。

from flask import Flask
from decorator import requires_auth 
app = Flask(__name__)

@app.before_request # <- 全てのviewで前処理を行うためのdecoratorを使った関数を用意
@requires_auth          #<- ここでBasic認証のdecoratorを使う
def before_request():
    pass

@app.route("/") # <- 個別のviewからはデコレータを外した。
def index():
    return "Hello Index!"

@app.route("/hello")
def hello():
    return "Hello World!"

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

これで サイト全体でBasic認証がかかるようになりました。Flaskが動く環境が有れば、Google App Engineとかでも大丈夫。