Djangoの認証システムを使用する¶
このドキュメントでは、デフォルト設定でのDjangoの認証システムの使用方法を説明します。この設定は、タスクの適切な範囲を管理することで、最も一般的なプロジェクトのニーズにかなうよう徐々に発展してきました。そして、パスワードや権限の入念な実装を持っています。デフォルトの認証システムからの変更が必要なプロジェクトのために、Djangoは認証システムの広範囲の 拡張とカスタマイズ をサポートします。
Djangoの認証は、認証機能と権限機能の両方を共に提供しています。そして、一般的に、これらの機能を合わせて認証システムと呼びます。
User オブジェクト¶
User オブジェクトは、認証システムの中核です。一般的に、このオブジェクトはあなたのサイトに関係する人々を表し、アクセスを制限すること、ユーザ情報を登録すること、コンテンツを作成者と関連付けることを可能にする際などに利用されます。
Djangoの認証フレームワークにはUserクラスという、ただひとつのクラスのみが存在します。すなわち、 'superusers' または admin 'staff' ユーザは、Userオブジェクトと異なるクラスではなく、特別な属性セットを持ったUserオブジェクトなのです。
デフォルトのユーザの主要な属性は次のとおりです。
仕様については full API documentation を参照してください。 以下のドキュメントは、よりタスク指向の形式となっています。
ユーザを作成する¶
ユーザを作成するための最も直接的な方法は、組み込まれている create_user() というヘルパー関数を利用することです。
>>> from django.contrib.auth.models import User
>>> user = User.objects.create_user('john', 'lennon@thebeatles.com', 'johnpassword')
# At this point, user is a User object that has already been saved
# to the database. You can continue to change its attributes
# if you want to change other fields.
>>> user.last_name = 'Lennon'
>>> user.save()
すでにDjango adminをインストールしている場合は、 インタラクティブにユーザを作成する こともできます。
スーパーユーザを作成する¶
Create superusers using the createsuperuser command:
$ python manage.py createsuperuser --username=joe --email=joe@example.com
パスワードを入力するように促されます。入力後、ただちにユーザが作成されます。 --username または --email オプションを使用しなければ、これらの値を入力するように促されます。
パスワードを変更する¶
Djangoはユーザモデルに未加工の (単なるテキストの) パスワードは保存せず、ハッシュ値でのみ保存します (詳細は、パスワードは管理方法に関するドキュメント を参照してください)。したがって、ユーザのパスワード属性を直接操作しないでください。これが、ユーザを作成する際にヘルパー関数を使用する理由です。
ユーザのパスワードを変更するには、いくつかのオプションがあります。
manage.py changepassword *username* は、コマンドラインからユーザのパスワードを変更する方法を提供します。ユーザのパスワードを変更するよう促されたら、パスワードを 2 回入力してください。2 つのパスワードが一致した場合、新しいパスワードが直ちに有効になります。ユーザを指定しない場合、コマンドは、現在のシステムユーザとユーザ名が一致するユーザのパスワードを変更するよう試みます。
set_password() を使用することで、プログラムでパスワードを変更することもできます:
>>> from django.contrib.auth.models import User
>>> u = User.objects.get(username='john')
>>> u.set_password('new password')
>>> u.save()
Django admin がインストールされていれば、 認証システムのadminページ にて、ユーザのパスワードを変更することも可能です。
また、Djangoはユーザ自身のパスワードを変更するための ビュー と フォーム を提供します。
ユーザーのパスワード変更を行う事とそのユーザーのセッションは全てログアウトされます。詳細は Session invalidation on password change を参照してください。
ユーザを認証する¶
-
authenticate(request=None, **credentials)¶ 認証情報のセットを検証するには
authenticate()を利用してください。このメソッドは認証情報をキーワード引数として受け取ります。検証する対象はデフォルトではusernameとpasswordであり、その組み合わせを個々の 認証バックエンド に対して問い合わせ、認証バックエンドで認証情報が有効とされればUserオブジェクトを返します。もしいずれの認証バックエンドでも認証情報が有効と判定されなければPermissionDeniedが送出され、Noneが返されます。以下は実装例です:from django.contrib.auth import authenticate user = authenticate(username='john', password='secret') if user is not None: # A backend authenticated the credentials else: # No backend authenticated the credentials
requestはオプションで、HttpRequestのインスタンスを取ります。このインスタンスは認証バックエンドのauthenticate()メソッドに渡されます。注釈
This is a low level way to authenticate a set of credentials; for example, it's used by the
RemoteUserMiddleware. Unless you are writing your own authentication system, you probably won't use this. Rather if you're looking for a way to login a user, use theLoginView.
権限と認可¶
Django comes with a built-in permissions system. It provides a way to assign permissions to specific users and groups of users.
これは、Djangoのadminサイトでも使われていますが、独自のコード内でも自由に使えます。
Djangoのadminサイトは、次のように権限を使用します:
- Access to view objects is limited to users with the "view" or "change" permission for that type of object.
- "追加"フォームのビューにアクセスし、オブジェクトの追加をすることは、そのオブジェクトの型への"追加"権限を持つユーザに限定されています。
- 変更リストを表示し、"変更"フォームを表示し、オブジェクトを変更することは、そのオブジェクトの型への"変更"権限を持つユーザーに限定されています。
- オブジェクトを削除することは、そのオブジェクトの型への"削除"権限を持つユーザに限定されています。
Permissions can be set not only per type of object, but also per specific
object instance. By using the
has_view_permission(),
has_add_permission(),
has_change_permission() and
has_delete_permission() methods provided
by the ModelAdmin class, it is possible to
customize permissions for different object instances of the same type.
User オブジェクトは 2 つの多対多のフィールド、groups および user_permissions を持っています。他の Django におけるモデル で行えるのと同様に User オブジェクトは関連を持っているオブジェクトにアクセスできます:
myuser.groups.set([group_list])
myuser.groups.add(group, group, ...)
myuser.groups.remove(group, group, ...)
myuser.groups.clear()
myuser.user_permissions.set([permission_list])
myuser.user_permissions.add(permission, permission, ...)
myuser.user_permissions.remove(permission, permission, ...)
myuser.user_permissions.clear()
デフォルトの権限¶
When django.contrib.auth is listed in your INSTALLED_APPS
setting, it will ensure that four default permissions -- add, change, delete,
and view -- are created for each Django model defined in one of your installed
applications.
これらの権限は manage.py migrate 実行時に作成されます。INSTALLED_APPS に django.contrib.auth を追加後初めての migrate を実行した場合は、新たにインストールされるモデルに対してと同様、それまでに作成されたモデルに対してもデフォルトの権限が作成されます。以後 manage.py migrate (この権限を作成する関数は post_migrate シグナルに接続されています)を実行する度作成されるモデルに対してデフォルトの権限が作成されます。
app_label が foo でモデルが Bar であるアプリケーションを想定し、デフォルトの権限を試すには以下を利用する必要が有ります:
- 追加:
user.has_perm('foo.add_bar') - 変更:
user.has_perm('foo.change_bar') - 削除:
user.has_perm('foo.delete_bar') - view:
user.has_perm('foo.view_bar')
Permission モデルに対して直接アクセスする事はほぼ有りません。
グループ¶
django.contrib.auth.models.Group モデルはユーザーを分類する一般的な方法で、対象となるユーザーに権限や、何らかの分類名を付けることが可能となります。個々のユーザーは複数のグループに属する事ができます。
グループに属するユーザーは、そのグループに対して許可されている権限を自動的に持つ事になります。例えば、Site editors グループが can_edit_home_page 権限を持っていた場合、そのグループに属する全てのユーザーはその権限を持つ事になります。
権限の管理に限らず、グループはユーザーに何らかの分類名や、拡張された機能を付与する上で有用です。例えば Special users というグループを作成して、そのグループのメンバーに対してサイトのメンバー限定の領域にアクセスする権限を付与したり、メンバー限定のメールを送るコードを書いたりする事も可能なのです。
プログラムによる権限作成¶
カスタム権限 はモデルの Meta クラス内に定義され、直接作成する事も可能です。例えば、myapp 内の BlogPost モデルに対する権限 can_publish は下記のように作成する事ができます:
from myapp.models import BlogPost
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
content_type = ContentType.objects.get_for_model(BlogPost)
permission = Permission.objects.create(
codename='can_publish',
name='Can Publish Posts',
content_type=content_type,
)
この権限は user_permissions 属性を介して User に、あるいは permissions 属性を介して Group に割り当てられます。
Proxy models need their own content type
If you want to create permissions for a proxy model, pass for_concrete_model=False to
ContentTypeManager.get_for_model() to get the appropriate
ContentType:
content_type = ContentType.objects.get_for_model(BlogPostProxy, for_concrete_model=False)
In older versions, proxy models use the content type of the concrete model.
権限のキャッシュ¶
ModelBackend はユーザーオブジェクトが権限の確認のため最初に要求した情報をキャッシュします。この仕組みはリクエスト-レスポンスのサイクルの中では(例えば管理機能によって)通常の場合、権限が付与されてから直ちに権限の確認が生じる事が無いため大抵の場合問題がありません。もし権限を付与した後直ちに権限の確認を行う場合、テストあるいは認証をビューで例示する場合等、最も簡単な解決手段はデータベースから再度ユーザーの情報を取得する事です。以下は実装例です:
from django.contrib.auth.models import Permission, User
from django.contrib.contenttypes.models import ContentType
from django.shortcuts import get_object_or_404
from myapp.models import BlogPost
def user_gains_perms(request, user_id):
user = get_object_or_404(User, pk=user_id)
# any permission check will cache the current set of permissions
user.has_perm('myapp.change_blogpost')
content_type = ContentType.objects.get_for_model(BlogPost)
permission = Permission.objects.get(
codename='change_blogpost',
content_type=content_type,
)
user.user_permissions.add(permission)
# Checking the cached permission set
user.has_perm('myapp.change_blogpost') # False
# Request new instance of User
# Be aware that user.refresh_from_db() won't clear the cache.
user = get_object_or_404(User, pk=user_id)
# Permission cache is repopulated from the database
user.has_perm('myapp.change_blogpost') # True
...
プロキシモデル¶
Proxy models work exactly the same way as concrete models. Permissions are created using the own content type of the proxy model. Proxy models don't inherit the permissions of the concrete model they subclass:
class Person(models.Model):
class Meta:
permissions = [('can_eat_pizzas', 'Can eat pizzas')]
class Student(Person):
class Meta:
proxy = True
permissions = [('can_deliver_pizzas', 'Can deliver pizzas')]
>>> # Fetch the content type for the proxy model.
>>> content_type = ContentType.objects.get_for_model(Student, for_concrete_model=False)
>>> student_permissions = Permission.objects.filter(content_type=content_type)
>>> [p.codename for p in student_permissions]
['add_student', 'change_student', 'delete_student', 'view_student',
'can_deliver_pizzas']
>>> for permission in student_permissions:
... user.user_permissions.add(permission)
>>> user.has_perm('app.add_person')
False
>>> user.has_perm('app.can_eat_pizzas')
False
>>> user.has_perms(('app.add_student', 'app.can_deliver_pizzas'))
True
In older versions, permissions for proxy models use the content type of the concrete model rather than content type of the proxy model.
Web のリクエストにおける認証¶
Django は リクエストオブジェクト に対して認証システムを接続させるのに セッション とミドルウェアを利用します。
それらは現在のユーザーを示す request.user 属性を付与します。もしユーザーが現在ログインしていない場合、この属性には AnonymousUser のインスタンスが、ログインしている場合は User のインスタンスがセットされます。
この二者は is_authenticated を用いて次のように識別する事ができます:
if request.user.is_authenticated:
# Do something for authenticated users.
...
else:
# Do something for anonymous users.
...
ユーザーをログインさせるには¶
現在のセッションにおいて認証を有効としたいユーザーがいる場合 - login() 関数によってそれを行う事ができます。
-
login(request, user, backend=None)¶ あるユーザーをログインさせる場合は、
login()を利用してください。この関数はHttpRequestオブジェクトとUserオブジェクトを受け取ります。login()は Django のセッションフレームワークを利用して、ユーザーのセッション中での ID を保持します。匿名ユーザーとしてのセッション中にセットされたデータが、ログイン後も継続して利用できる事に注意してください。
以下の例では
authenticate()およびlogin()をどのように用いるかを示します:from django.contrib.auth import authenticate, login def my_view(request): username = request.POST['username'] password = request.POST['password'] user = authenticate(request, username=username, password=password) if user is not None: login(request, user) # Redirect to a success page. ... else: # Return an 'invalid login' error message. ...
認証バックエンドの選択¶
ユーザーがログインする際、そのユーザーの ID と認証時に用いた認証バックエンドはセッション中保持されます。その仕組みによって、ユーザーの詳細情報を取得するリクエストが発生した場合に同じ 認証バックエンド を利用できます。セッション中に保持される認証バックエンドは下記の手順を経て選択されます:
- 省略可能な
backend引数が与えられている場合は利用します。 - 存在すれば属性
user.backendの値を利用する。authenticate()は返すユーザーオブジェクトに属性値user.backendを付与するので、authenticate()とlogin()とで連携を図ることができる。 - ただ一つだけ設定が存在すれば
AUTHENTICATION_BACKENDSのbackendを利用する。 - いずれにも該当しなかった場合、例外が送出される。
1 もしくは 2 においては、引数 backend あるいは属性値 user.backend は(AUTHENTICATION_BACKENDS で定義されているのと同様に)ドット付きのインポート先を示すパスの文字列でなければなりません。
ユーザーをログアウトさせるには¶
-
logout(request)¶ django.contrib.auth.login()を利用してログインしたユーザーをログアウトさせるためには、django.contrib.auth.logout()をビューの中で利用してください。この関数はHttpRequestオブジェクトを受け取り、値を返しません。実装例は下記のようになります:from django.contrib.auth import logout def logout_view(request): logout(request) # Redirect to a success page.
logout()は対象となるユーザーが最初からログインしていなかった場合でも例外を送出しない事に注意してください。logout()を呼び出すと、現在処理しているリクエストに対応したセッション情報は完全に破棄されます。その時点までに存在している全てのデータが削除されます。これは、別の人物が同じウェブブラウザを利用してログインし、前のユーザーのセッションにアクセスして使用してしまう事態を防ぐためです。ログアウトした直後でも利用できる何らかの情報をセッションに保存したい場合は、django.contrib.auth.logout()を呼び出した 後 に行ってください。
ログインしているユーザーにアクセスを制限する¶
原理的な方法¶
The raw way to limit access to pages is to check
request.user.is_authenticated and either redirect to a
login page:
from django.conf import settings
from django.shortcuts import redirect
def my_view(request):
if not request.user.is_authenticated:
return redirect('%s?next=%s' % (settings.LOGIN_URL, request.path))
# ...
もしくはエラーメッセージを出力します。
from django.shortcuts import render
def my_view(request):
if not request.user.is_authenticated:
return render(request, 'myapp/login_error.html')
# ...
login_required デコレータ¶
-
login_required(redirect_field_name='next', login_url=None)¶ ショートカットとして、便利な
login_required()デコレータを利用できます:from django.contrib.auth.decorators import login_required @login_required def my_view(request): ...
login_required()は下記の処理を行います:- もしユーザがログインしていなければ、
settings.LOGIN_URLにリダイレクトし、クエリ文字列に現在の絶対パスを渡します。リダイレクト先の例:/accounts/login/?next=/polls/3/ - もしユーザがログインしていれば、通常通りビューを処理します。ビューのコードの中ではユーザがログインしているかを意識しなくて良いのです。
デフォルトでは、認証に成功したユーザがリダイレクトされる先のパスは
"next"という名称のクエリパラメータに格納されています。もし異なるパラメータ名を利用したい場合、login_required()がredirect_field_nameという省略可能な引数を受け取ります:from django.contrib.auth.decorators import login_required @login_required(redirect_field_name='my_redirect_field') def my_view(request): ...
redirect_field_nameに値を持たせた場合、ログインテンプレートもカスタマイズする必要があるでしょう。これは、リダイレクト先のパスを格納しているテンプレートコンテキスト変数が、キーとして (デフォルトの)"next"でなくredirect_field_nameの値を使用してしまうためです。login_required()はまた省略可能な引数としてlogin_urlを受け取る事ができます。以下の例のように利用します:from django.contrib.auth.decorators import login_required @login_required(login_url='/accounts/login/') def my_view(request): ...
`login_url`のパラメータを定義しない場合、settings.LOGIN_URLが設定されかつログイン用ビューが適切に配置されている必要が有ります。例えば、デフォルトの設定を利用して下記の内容を URLconf に追加してください:from django.contrib.auth import views as auth_views path('accounts/login/', auth_views.LoginView.as_view()),
settings.LOGIN_URLはまたビュー関数名と 命名された URL パターン を受け付けます。この仕組みによって設定を更新することなく URLconf 内のログイン用ビューを再配置する事ができます。- もしユーザがログインしていなければ、
注釈
login_required デコレータはユーザーのフラグ is_active をチェックしませんが、デフォルトの AUTHENTICATION_BACKENDS はアクティブでないユーザを拒否します。
参考
もし Django の管理画面にカスタマイズしたビューを実装している(あるいはビルトインのビューが利用しているのと同じ認証チェックが必要である)場合は、django.contrib.admin.views.decorators.staff_member_required() デコレータが login_required() の代替として有用であるはずです。
LoginRequired mixin¶
クラスベースのビュー を使う際、 LoginRequiredMixin を使うことで login_required と同じ動作をさせることができます。 この mixin は、継承リストの一番左に記述される必要があります。
-
class
LoginRequiredMixin¶ ビューがこの mixin を使う場合、認証されていないユーザによるすべてのリクエストは、ログインページにリダイレクトされるか、HTTP 403 Forbidden エラー表示となります。これは、
raise_exceptionパラメータにて設定します。AccessMixinのパラメータをセットすると、認証されていないユーザの管理をカスタムできます:from django.contrib.auth.mixins import LoginRequiredMixin class MyView(LoginRequiredMixin, View): login_url = '/login/' redirect_field_name = 'redirect_to'
注釈
login_required デコレータと同様に、この mixin はユーザの is_active フラグをチェックしません。しかし、デフォルトの AUTHENTICATION_BACKENDS が非アクティブのユーザを拒否します。
テストをパスしたログイン済みユーザのアクセスを制限する¶
To limit access based on certain permissions or some other test, you'd do essentially the same thing as described in the previous section.
You can run your test on request.user in
the view directly. For example, this view checks to make sure the user has an
email in the desired domain and if not, redirects to the login page:
from django.shortcuts import redirect
def my_view(request):
if not request.user.email.endswith('@example.com'):
return redirect('/login/?next=%s' % request.path)
# ...
-
user_passes_test(test_func, login_url=None, redirect_field_name='next')¶ As a shortcut, you can use the convenient
user_passes_testdecorator which performs a redirect when the callable returnsFalse:from django.contrib.auth.decorators import user_passes_test def email_check(user): return user.email.endswith('@example.com') @user_passes_test(email_check) def my_view(request): ...
user_passes_test()takes a required argument: a callable that takes aUserobject and returnsTrueif the user is allowed to view the page. Note thatuser_passes_test()does not automatically check that theUseris not anonymous.