Python | Web フレームワーク:Django 認証

Python
スポンサーリンク

概要(Django 認証=「ユーザーを識別して、ログイン状態を管理する仕組み」)

Django 認証は、

「このリクエストを送ってきたのは誰か」
「その人はログイン済みか」
「その人にこの操作をさせてよいか」

を判断するための仕組みです。

もっと砕くと、

ユーザー情報を保存するための User モデル
パスワードを安全に保存・照合する仕組み
ログイン・ログアウトを行う関数
ログインしていないと見せたくないページを守るための仕組み

が一式そろっている「認証・認可フレームワーク」です。

Django はこれを標準装備しているので、
自分で「ログイン機能」をゼロから作る必要はありません。

Django 認証の全体像をざっくりつかむ

User モデルと認証バックエンドのイメージ

Django には標準で User モデルが用意されています。
django.contrib.auth アプリの中にあるモデルで、
ユーザー名、メールアドレス、パスワード(ハッシュ)、is_staff、is_superuser などを持っています。

認証バックエンドは、

「ユーザー名とパスワードを受け取って、
それが正しければ User オブジェクトを返す」

という役割のクラスです。
通常は Django 標準のバックエンドをそのまま使いますが、
独自の認証方式(例えばメールアドレスでログイン)を作りたいときは
ここをカスタマイズします。

リクエストが来たとき、Django はセッション情報を見て、
「このセッションはどのユーザーに紐づいているか」を判断し、
request.user に User オブジェクト(または AnonymousUser)をセットします。

ビュー側は、
この request.user を見ることで
「ログインしているか」「誰なのか」を知ることができます。

セッションとクッキーで「ログイン状態」を覚えておく

ログインは一度きりのイベントではなく、
「ログインしたあと、次のリクエストでもログイン状態を維持する」
必要があります。

Django は、セッションとクッキーを使ってこれを実現しています。

ユーザーがログインすると、
Django はセッション ID を発行し、それをクッキーに保存します。
次のリクエストでは、そのクッキーを見て
「このセッションはユーザー X に対応している」と判断し、
request.user にそのユーザーをセットします。

開発者としては、
セッションやクッキーを直接意識する場面は少なく、
基本的には login()logout() を呼び、
request.user を見るだけで済みます。

ログイン・ログアウトの基本的な流れ

ログインビューの典型的な書き方

最も基本的なログイン処理の流れを、
あえて「自前でフォームを書く」形で見てみます。

まず、ビューです。

# accounts/views.py
from django.shortcuts import render, redirect
from django.contrib.auth import authenticate, login, logout

def login_view(request):
    if request.method == "POST":
        username = request.POST.get("username")
        password = request.POST.get("password")

        user = authenticate(request, username=username, password=password)
        if user is not None:
            login(request, user)  # セッションにユーザー情報を保存
            return redirect("home")
        else:
            context = {"error": "ユーザー名かパスワードが違います。"}
            return render(request, "accounts/login.html", context)

    return render(request, "accounts/login.html")
Python

ここで重要な関数が二つあります。

一つ目は authenticate()
これは「ユーザー名とパスワードが正しいかどうか」をチェックし、
正しければ User オブジェクトを返し、
間違っていれば None を返します。

二つ目は login()
これは「このリクエストを送ってきたブラウザを、
今認証されたユーザーと結びつける」処理です。
内部的にはセッションを作り、クッキーにセッション ID をセットします。

テンプレートはシンプルで構いません。

<!-- templates/accounts/login.html -->
<h1>ログイン</h1>

{% if error %}
  <p style="color:red;">{{ error }}</p>
{% endif %}

<form method="post">
  {% csrf_token %}
  <p>
    <label>ユーザー名: <input type="text" name="username"></label>
  </p>
  <p>
    <label>パスワード: <input type="password" name="password"></label>
  </p>
  <button type="submit">ログイン</button>
</form>
Python

このログインに成功すると、
以降のリクエストでは request.user
そのユーザーを指すようになります。

ログアウトビューはとてもシンプル

ログアウトはもっと簡単です。

def logout_view(request):
    logout(request)
    return redirect("home")
Python

logout() は、
現在のセッションからユーザー情報を削除し、
ログイン状態を解除します。

これで、次のリクエストからは request.user が AnonymousUser になります。

ログイン必須ページを守る(login_required と request.user)

デコレータで「ログインしていないと見せない」ページを作る

「ログインしている人だけ見られるページ」を作りたいとき、
毎回ビューの先頭で

if not request.user.is_authenticated:
    ...
Python

と書くのは面倒です。

Django には login_required というデコレータが用意されています。

from django.contrib.auth.decorators import login_required
from django.shortcuts import render

@login_required
def mypage(request):
    return render(request, "accounts/mypage.html")
Python

このように書くと、
ログインしていないユーザーがこのビューにアクセスしたとき、
自動的にログインページにリダイレクトされます。

どの URL にリダイレクトするかは、
settings.LOGIN_URL で設定できます。
デフォルトは /accounts/login/ です。

この仕組みのおかげで、
「ログイン必須ページ」を簡単に守ることができます。

request.user を使って「誰のページか」を判断する

ログインしているユーザーは、
ビューの中で request.user から参照できます。

例えば、「自分のプロフィールページ」を表示するビューは
こう書けます。

@login_required
def profile(request):
    user = request.user
    return render(request, "accounts/profile.html", {"user": user})
Python

テンプレートでは、

<p>ユーザー名: {{ user.username }}</p>
<p>メール: {{ user.email }}</p>

のように表示できます。

ここでのポイントは、

ログインしているユーザーを特別扱いする必要はなく、
常に request.user を見ればよい

ということです。

ログインしていない場合は request.user.is_authenticated が False で、
ログインしている場合は True になります。

認可(権限)とグループのイメージ

is_staff, is_superuser, permissions

Django の User モデルには、
いくつかの「権限」に関するフラグがあります。

is_staff は「管理サイトにログインできるかどうか」
is_superuser は「全ての権限を持つスーパーユーザーかどうか」

さらに、
モデルごとの「追加・変更・削除・閲覧」権限もあります。
これらは管理画面からユーザーごとに設定できます。

ビューの中で、

if request.user.is_staff:
    ...
Python

のようにチェックしたり、
user.has_perm("app.change_book") のように
特定の権限を持っているかどうかを確認することもできます。

これが「認証(誰か)」ではなく「認可(何をしてよいか)」の部分です。

グループで権限をまとめる

ユーザーが増えてくると、
一人ひとりに権限を設定するのは大変です。

Django には「グループ」という概念があり、
グループに権限を付けておき、
ユーザーをそのグループに所属させることで
権限をまとめて管理できます。

例えば、「編集者」グループに
記事の追加・変更権限を付けておき、
編集者ユーザーをそのグループに入れる、
といった使い方です。

これらは主に管理画面で操作しますが、
ビューの中で request.user.groups を見ることもできます。

認証を「自前で作らない」ことの大切さ

パスワードを平文で保存してはいけない

初心者がやりがちな危険パターンとして、
自前で users テーブルを作り、
パスワードをそのまま保存してしまう、
というものがあります。

これは絶対にやってはいけません。

Django の User モデルは、
パスワードをハッシュ化して保存し、
ログイン時にはそのハッシュを使って照合します。
生のパスワードは保存しません。

user.set_password("raw_password") を呼ぶと、
内部で安全なハッシュが計算され、
user.password にはハッシュ文字列だけが保存されます。

authenticate() は、
入力されたパスワードを同じ方法でハッシュ化し、
保存されているハッシュと一致するかどうかを確認します。

このあたりを自前で実装しようとすると、
簡単にセキュリティホールを作ってしまいます。

だからこそ、

ログイン機能は Django の認証フレームワークに乗せる
User モデルを自前でゼロから作らない

という方針がとても重要です。

カスタム User モデルが必要になるのは「もう少し先」でいい

Django では、
標準の User モデルを置き換える「カスタムユーザーモデル」も作れます。

ただし、これは少し難易度が上がります。
初心者のうちは、

標準の User モデルをそのまま使う
足りない情報は Profile モデルなどで拡張する

という形で十分です。

認証の仕組みそのものをいじるのではなく、
「認証されたユーザーに紐づく追加情報」を
別モデルで持つ、という設計にすると、
安全でシンプルな構成になります。

まとめ(Django 認証は「ログイン機能を丸ごと任せられる土台」)

Django 認証を初心者目線で整理すると、こうなります。

User モデルと認証バックエンド、セッションを組み合わせて、「誰がログインしているか」を自動で管理し、ビューからは request.user を見るだけでよい。
authenticate() でユーザー名とパスワードをチェックし、login() でセッションにユーザーを紐づけ、logout() でログイン状態を解除する、というのがログイン・ログアウトの基本パターン。
login_required デコレータや request.user.is_authenticated を使うことで、「ログインしている人だけ見られるページ」を簡単に作れる。
パスワードのハッシュ化や権限管理など、危険で難しい部分は Django が全部面倒を見てくれるので、「認証はフレームワークに任せる」という姿勢がとても大事。

タイトルとURLをコピーしました