Djangoフレームワークの
ユーザーモデルと認証
PyConJP 2017
岡野 真也 (@tokibito)
お前誰よ?
岡野真也 (@tokibito)
株式会社オープンコレクター
http://www.open-c.jp/
お仕事あればご相談ください
2
今日話すこと
認証と認可について
前知識
『Djangoの認証機能』
認証機能の使い方
認証機能の仕組み
認証機能のカスタマイズ
Djangoのバージョンは、1.11を前提としています。
3
認証と認可について
4
Auth
5
Auth?
認証(Authentication)
システムから見て、利用者が誰であるかを判別
して扱うこと
この話をします
認可(Authorization)
システム上で、利用者に閲覧や操作を許可する
こと
権限を扱うこと
この話はしません (hirokikyさんのセッションへ)
6
認証の話に入る前に
Webアプリケーションで認証を扱うための前知識
HTTPヘッダ
Cookie
セッション
ざっくり説明
(省略) ブラウザ=ウェブブラウザ(ChromeとかIE)
(省略) サーバー=HTTPサーバー
7
HTTPヘッダ
HTTPでのリクエスト, レスポンスの先頭のほうに
ある情報
ブラウザの画面上には表示されない
ブラウザがコンテンツをよしなに扱うための情報
が詰まってる
ブラウザの開発者ツールで確認できるよ
8
ブラウザとサーバーのやりと
り
9
Cookie
ブラウザにデータを保存できるところ
キーと値のペアで保存
サーバーからのレスポンスでHTTPヘッダにSet-
Cookieでデータを入れとくと、 ブラウザが保存し
てくれる
保存されたデータは、ブラウザが次回リクエスト
時に Cookieヘッダでサーバーに送信される
10
ブラウザとのCookieのやりと
り
11
セッション(HTTPセッション)
ブラウザからの操作(ページ遷移)を 一連の流れ と
して扱うこと
セッションに紐付けて、サーバー側で認証情報な
どを保持したりできる(セッションデータ)
12
セッションの仕組み(例)
1. ブラウザがサーバーへHTTPリクエスト
2. サーバー側でセッションIDを発行し、Set-Cookie
ヘッダに含めてレスポンス
3. ブラウザはCookieデータとしてセッションIDを保
存
4. 次回以降、ブラウザのHTTPリクエストでCookie
ヘッダでセッションIDをサーバーに送信
5. サーバーはセッションIDが一致すれば同一セッシ
ョンとして扱う
13
セッションのやりとり
14
ここからは『Djangoの』話
15
Djangoのセッション機能
django.contrib.session (デフォルトで有効)
Cookieでは sessionid キーでセッションIDを保持
SessionMiddlewareにてCookieのセッションIDと
サーバー上のセッションIDを照合
サーバー上に保存されたセッションデータは、
request.session 辞書に復元される
認証データはここに格納します
16
Djangoのセッション
17
ここからDjangoの認証の話
18
まずは使い方から。
19
Djangoの認証機能の使い方
アプリケーションの有効化
設定
ユーザーの登録
ログイン、ログアウトのView
Viewでユーザー情報を使う
ログインが必要なViewを作る
ユーザーに紐づくデータ
API
20
アプリケーションの有効化
Djangoプロジェクトの settings.py の
INSTALLED_APPSに django.contrib.auth を記述
デフォルトで有効
MIDDLEWAREに
django.contrib.auth.middleware.AuthenticationMiddleware
を記述
デフォルトで有効
manage.py migrate でユーザー情報を保存するテー
ブルを作成
21
設定
ログイン後のリダイレクト先の設定
settings.LOGIN_REDIRECT_URL
ログアウト後のリダイレクト先の設定
settings.LOGOUT_REDIRECT_URL
ログインページのURL設定
settings.LOGIN_URL
22
ユーザーの登録
管理者ユーザーの登録
manage.py スクリプトの createsuperuser コマン
ド
./manage.py createsuepruser
一般ユーザーの登録
Djangoの管理画面から登録
Userモデルを操作して登録
23
24
ログイン、ログアウトのView
urls.py で django.contrib.auth.views.LoginView を使い
ます。ログアウトは LogoutView です。
from django.conf.urls import url
from django.contrib.auth.views import LoginView, LogoutView
urlpatterns = [
url(r'^login$', LoginView.as_view(), name='login'),
url(r'^logout$', LogoutView.as_view(), name='logout'),
]
ログイン画面のデフォルトテンプレートパスは、
registration/login.html
25
Viewでユーザー情報を使う
Viewでは request.user を参照します。
未認証時は、 AnonymousUser 、認証済みの場合は User
のインスタンスとなります。
from django.http import HttpResponse
def my_view(request):
if request.user.is_authenticated:
return HttpResponse("認証済みです: {}".format(
request.user.username))
else:
return HttpResponse("未認証です")
26
ログインが必要なViewを作る
ログインを必須とするViewを作る場合は、
django.contrib.auth.decorators.login_required デコレ
ータを使います。
from django.contrib.auth.decorators import login_required
@login_required
def secret_view(request):
"認証が必要なView"
未認証時にアクセスすると、ログインURLへリダイレ
クトされます。
27
ユーザーに紐づくデータ
ユーザーに関連するデータを保存するモデルを定義す
る場合は、ForeignKeyには settings.AUTH_USER_MODEL
を指定します。
from django.db import models
from django.conf import settings
class Article(models.Model):
author = models.ForeignKey(
settings.AUTH_USER_MODEL, on_delete=models.CASCADE,
)
ユーザーモデルは、 settings.AUTH_USER_MODEL で変更
できるためです。 28
API
認証機能に依存するアプリケーションを作る際には
APIを使います。以下は、特定のユーザーでログイン
させる例です。
from django.shortcuts import redirect
from django.contrib.auth import get_user_model, login
def my_view(request):
UserModel = get_user_model() # ユーザーモデルの取得
user = UserModel.objects.get(pk=1) # pk=1のユーザーを取得
login(request, user) # 取得したユーザーでログインさせる
return redirect('/') # リダイレクト
その他API多数。詳しくは ドキュメント を参照。
29
使い方はここまで。
30
Djangoの認証機能の仕組み
31
ログイン処理のサーバー側
ざっくり説明すると、
1. ユーザーの照合処理
1-1. ユーザー名でDBからユーザー情報を探す
1-2. パスワード照合
1-3. 有効かどうかチェック
ここまでに失敗したらログイン画面再表示
2. セッションにユーザーIDと使用したバックエンド
クラス名を保存
3. リダイレクトレスポンスを返す
32
ログイン処理の流れ
33
ユーザーの照合処理の詳細
settings.AUTHENTICATION_BACKENDSに列挙され
たクラスが使われます
django.contrib.auth.backends.ModelBackend (抜粋)
class ModelBackend(object):
def authenticate(self, request, username, password):
"""usernameでユーザー情報を取得
passwordが正しければUserオブジェクトを返す"""
def get_user(self, user_id):
"""user_idと一致するUserオブジェクトを返す"""
34
ユーザー情報
django.contrib.auth.models.User (抜粋)
class AbstractBaseUser(models.Model):
password = models.CharField()
class AbstractUser(AbstractBaseUser, PermissionsMixin):
username = models.CharField()
class User(AbstractUser):
class Meta(AbstractUser.Meta):
swappable = 'AUTH_USER_MODEL'
Userモデルにusernameとpasswordを保持しています
35
セッションへの保存
認証に成功すると、ユーザー情報はセッションに保存
されます
request.session["_auth_user_id"] = user.pk
request.session["_auth_user_backend"] = '(バックエン
ドクラス名)'
セッション機能によりこのデータは、サーバー側で保
存されます。
36
認証できました
ここまでの処理:
セッションにユーザーID(認証データ)が保持された
ログイン処理が成功したのでリダイレクトされた
次は認証データを使う処理を見ていきます。
37
認証データの参照
認証データを参照する処理は、次のような仕組みで動
作します。
1. AuthenticationMiddleware
1-1. request.sessionからユーザーIDと認証バック
エンドクラス名を取り出す
1-2. バックエンドクラスでUserを取得
1-3. request.user にUserかAnonymousUserを代入
2. Viewで request.user を参照する
38
認証データの参照
39
セキュリティTips
複数のブラウザで同じユーザーにて同時にログイ
ンは可能。
パスワードを変更した場合は、変更処理をした
セッション以外のログイン情報は無効化される
パスワードの暗号化処理は、カスタマイズ可能。
デフォルトのアルゴリズムはPBKDF2。
40
認証機能のカスタマイズ
41
よくある要求
ユーザー情報のカラムを増やしたい
ユーザーモデルのカスタマイズ
LDAPやAD認証など他の認証システム(照合のみ)と
連携したい
バックエンド変更
OAuth(OpenID Connect)で外部の認証システムと
連携したい
python-social-authを使うだけでOK
もしくは、Viewを用意+バックエンド変更
42
カスタマイズのポイント
ユーザーモデルの変更
認証バックエンドの変更
43
ユーザーモデルの変更
AbstractBaseUser を継承してモデルを定義する
PK(ID)は必須
複合主キーは使えません。
class MyUser(AbstractBaseUser):
identifier = models.CharField(max_length=40, unique=True
...
USERNAME_FIELD = 'identifier'
settings.AUTH_USER_MODEL にクラスを指定すると有効化
詳しくは ドキュメント を参照。
44
認証バックエンドの変更
authenticateとget_userメソッドを持ったバックエン
ドクラスを定義して使います。
class MyBackend(object):
def authenticate(request, **credentials):
"照合OKならUserを返す処理を書く"
def get_user(self, user_id):
"user_idと一致するUserオブジェクトを返す処理を書く"
settings.AUTH_BACKENDS にクラスを指定すると有効化
詳しくは ドキュメント を参照。
45
まとめ
認証(Authentication)は、システムから見て、利用
者が誰であるかを判別して扱うこと
Djangoの認証機能では、セッションにユーザー情
報を保持している
Djangoの認証機能は、認証バックエンドでユーザ
ーの照合、取得をしている
ユーザーモデルや認証バックエンドはカスタマイ
ズできる
46

Djangoフレームワークのユーザーモデルと認証