デコレータ/ミドルウェアを使わずに認証処理を強制する
APIの認証処理にデコレータ・ミドルウェアを使用する事が多いと思います、デコレータ・ミドルウェアみたいな難しい概念を使わずに認証処理を忘れないで書く方法を紹介します。
主処理の引数に認証済みの証拠として Verified 構造体(クラス)を引数として必要とするように作ります。
デコレータ/ミドルウェアを使用しない認証処理コード
from flask import Flask, request, jsonify, abort
from dataclasses import dataclass
app = Flask(__name__)
# 認証済みを保証する構造体
@dataclass(frozen=True)
class Verified:
user_id: str
def authenticate_request() -> Verified:
"""リクエストを検証し、Verified を返す。失敗なら abort。"""
token = request.headers.get("Authorization")
if token == "SECRET_TOKEN":
# 実際はDBやJWTを検証してユーザー情報を取り出す
return Verified(user_id="user123")
abort(401, description="Unauthorized")
# 主処理: Verified を引数として要求する
def do_private_action(verified: Verified):
# verified が必須なので、認証をスキップできない
return {"message": f"Hello, {verified.user_id}!"}
@app.route("/private")
def private_endpoint():
verified = authenticate_request()
# 引数として verified を要求するため authenticate_request を書かざるを得ない
result = do_private_action(verified)
return jsonify(result)
@app.route("/public")
def public_endpoint():
return jsonify(message="This is a public endpoint.")
if __name__ == "__main__":
app.run(debug=True)
デコレータを使用した認証処理のサンプル
from flask import Flask, request, jsonify, abort
from functools import wraps
app = Flask(__name__)
# 認証デコレータのサンプル
def require_auth(f):
@wraps(f)
def decorated(*args, **kwargs):
token = request.headers.get("Authorization")
if token != "SECRET_TOKEN": # 本来はDBや認証サーバで検証
abort(401, description="Unauthorized")
return f(*args, **kwargs)
return decorated
@app.route("/public")
def public_endpoint():
return jsonify(message="This is a public endpoint.")
@app.route("/private")
@require_auth
def private_endpoint():
return jsonify(message="This is a protected endpoint.")
if __name__ == "__main__":
app.run(debug=True)
メリットとか
- pytest のモック試験とかやりやすくなる
- デコレータ/ミドルウェアという暗黙実行部分が減るだけでソースコードを追いかけるストレスが減る
あたりかなと思います。
以上です。