0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【webアプリ超初心者】flaskアプリにログインシステムを導入する方法

Posted at

はじめに

webアプリを作っていると、認証システムが必要になるケースがあります。

  • 登録されているユーザだけがアクセスできるようにしたい
  • ユーザごとにデータを切り分けて管理したい

こういったケースで、flask_loginを導入することで既存のFlaskアプリケーションに簡単にログイン機能を追加できます。

考え方

flask_loginを使うことで、ユーザIDとパスワードによる認証機能を既存のFlaskアプリに追加できます。
具体的には、LoginManagerが保護したいエンドポイントに対して、認証されたユーザにしかアクセスできないよう制限をかけ、ログイン画面へのリダイレクトを行います。
ユーザIDとパスワードのデータはデータベースに保存します。

データベースの操作について、SQLite3を使用する方法もありますが、今回は外部DBへの移行も想定してsqlalchemyを使用します。

以下Geminiを使って作成したコードのFlask-login導入に関係する部分を抜き出し、説明します。

導入方法

flask, flask_login, sqlite3をpipでインストールします。

pip install flask
pip install flask-login
pip install flask_sqlalchemy
pip instal sql_alchemy

必要なパッケージのimportをします。

import uuid
from flask import Flask, redirect, url_for, request
from flask_login import (
    LoginManager,
    UserMixin,
    login_user,
    logout_user,
    login_required,
    current_user,
)
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.orm import DeclarativeBase # SQLAlchemy 2.0+ 互換性のため

アプリケーションを新規に作成している場合はアプリケーションの初期化部分を記述する必要があります。既存のflaakアプリに追加する場合は不要です。

# --- アプリケーションと構成の初期化 ---
app = Flask(__name__)
# 開発・デモ用の一時的なシークレットキー。
app.secret_key = str(uuid.uuid4()) 
app.config['DEBUG'] = True

データベース操作についてはSQLAlchemyを使用します。
当然DBの配置を指定する必要があります。DB自体にはSQLiteを使用しています。
LoginManagerクラスのインスタンスを取得し、初期化します。

# --- SQLAlchemy 構成と初期化 ---
# データベース接続URIを設定 (SQLiteを使用)
# 外部データベース (PostgreSQLなど) に移行する際は、この行だけを変更すればOKです
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///app_sqlalchemy.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False # 推奨設定

# SQLAlchemyのベースクラスを定義 (2.0以降の標準的な方法)
class Base(DeclarativeBase):
    pass

db = SQLAlchemy(app, model_class=Base)

# Flask-Loginの初期化
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'login_page'
login_manager.login_message = "このページにアクセスするにはログインが必要です。"

UserMixinを継承したUserクラスを定義します。これを使ってUserオブジェクトを作り、UserIDからアクセスできるようにMAPに保管しておくことで、Flask_loginはセッションに保存された user_id からユーザー情報を復元できるようになります。

現在は"testuser"と"admin"の2つのユーザーしか管理していません。
新規ユーザーの登録については、現状では実装できていません。

load_user()メソッドのデコレーター付け忘れに注意。

# ---  ユーザーモデルとダミーデータベース (認証用) ---

# UserMixinを継承し、Flask-Loginに必要なプロパティを追加
class User(UserMixin):
    def __init__(self, id, username, password_hash):
        self.id = id
        self.username = username
        self.password_hash = password_hash
    
    # Flask-LoginがセッションIDとして使用するため、必ず文字列で返す
    def get_id(self):
        return str(self.id)

# ダミーのユーザーデータ (認証の確認のみに使用)
USER_ID_MAP = {
    1: User(1, "testuser", "password123"), # ID: 1
    2: User(2, "admin", "adminpass"),     # ID: 2
}
USERNAME_MAP = {u.username: u for u in USER_ID_MAP.values()}


# ユーザーIDからユーザーオブジェクトをロードする関数
@login_manager.user_loader
def load_user(user_id):
    """ユーザーIDに基づいてユーザーオブジェクトを返す"""
    try:
        user_id_int = int(user_id)
        return USER_ID_MAP.get(user_id_int)
    except ValueError:
        return None

ここでの説明は割愛しますが、HTMLとCSSでうまくUIを作ります。render_html()メソッドで共通のUIを先に定義しておくと楽に構成できるようです。

# --- HTML テンプレート(単一ファイル構成のためPython内で定義可能) ---

def render_html(title, body_content, current_user_status):
    """共通のHTML構造を生成する関数 (Tailwind CSSを使用)"""
    tailwind_cdn = '<script src="https://cdn.tailwindcss.com"></script>'

    return f"""
    """

ここからflaskアプリのルーティングを定義していきます。

# --- 6. ルーティングの定義 ---

@app.route('/')
def home():
    """公開されているホーム画面"""
    is_authenticated = current_user.is_authenticated
    username = current_user.username if is_authenticated else "ゲスト"
    
    status_tag = f"""
    """
    
    content = f"""
    """
    return render_html("ホーム", content, status_tag)


@app.route('/login', methods=['GET', 'POST'])
def login_page():
    """ログイン画面とログイン処理"""
    if current_user.is_authenticated:
        return redirect(url_for('dashboard'))

    message = ""
    if request.method == 'POST':
        username = request.form.get('username')
        password = request.form.get('password')
        remember = bool(request.form.get('remember'))

        user = USERNAME_MAP.get(username)

        # 認証ロジック
        if user and user.password_hash == password:
            login_user(user, remember=remember)
            
            next_page = request.args.get('next')
            return redirect(next_page or url_for('dashboard'))
        else:
            message = '<p class="text-red-500 font-medium mb-4">ユーザー名またはパスワードが違います。</p>'

    content = f"""
    """
    return render_html("ログイン", content, "")


@app.route('/dashboard')
@login_required 
def dashboard():
    """ログインが必須の保護されたページで、SQLAlchemyからユーザーデータを読み込む"""
    user_id = int(current_user.id) # DB検索のためにint型に変換
    username = current_user.username
    
    # ★SQLAlchemyによるデータ取得/初期化★
    
    # ユーザーのゲームデータを主キー(user_id)で検索
    user_data = db.session.get(GameData, user_id)

    # データが存在しない場合は、初期データを挿入する
    if user_data is None:
        user_data = GameData(user_id=user_id, score=0, level=1)
        db.session.add(user_data)
        db.session.commit()
    
    score = user_data.score
    level = user_data.level
    
    # ★スコアをランダムに更新するデモロジックを追加★
    import random
    if random.random() < 0.2: # 20%の確率でスコアを増やす
        user_data.score += random.randint(10, 50)
        db.session.commit() # 更新をコミット

    status_tag = f"""
        {username} 様
        <a href="{url_for('logout_page')}" class="bg-red-600 hover:bg-red-700 text-white py-1 px-3 rounded-md transition ml-4">ログアウト</a>
    """
    
    content = f"""
    """
    return render_html("ダッシュボード (SQLAlchemy連携)", content, status_tag)


@app.route('/logout')
@login_required
def logout_page():
    """ログアウト処理"""
    logout_user()
    return redirect(url_for('home'))

@login_manager.unauthorized_handler
def unauthorized():
    """未認証のアクセスがあった場合の処理"""
    return redirect(url_for('login_page', next=request.path))

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

実装例

以上を実装したサンプルアプリをGithubで公開しています。

応用・拡張

flask_loginを導入したことで、アプリの履歴データをユーザごとに切り分けて保存することが可能になります。その場合は、DB上にlogin用テーブルの他に、履歴データのテーブルを追加することになります。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?