はじめに
Django REST framework(以下DRF)のPermissionを開発に用いる中で、公式ドキュメントをもとに理解したことをまとめています。
Permissionの設定自体はシンプルですが、その設定で何が変わるか?どういうときに使うか?という観点も含めて開発するなかで得た学びを織り交ぜながらまとめます。
Permissionの理解を重視しているため、公式ドキュメントにある一部の設定方法は省略していることをご了承ください。
この記事の対象者
- DRFに入門した方
- DRF公式ドキュメントの理解を進めたい方
AuthenticationとPermissionの違い
DRFの公式ドキュメントでは、Permissionの説明にAuthenticationという言葉が登場します。まずはこの二つの違いについて簡単に整理します。一言で表すと、Authenticationではユーザーが誰であるかを確認し、 PermissionではユーザーがAPIにアクセスする権限を持つか確認します。
Authenticationでは送られてきた認証情報(セッションIDやトークンなど)をもとに、ユーザーが誰であるかを特定します。ユーザーの特定に成功した場合、request.userとしてそのユーザーがセットされ次の処理に移ります。認証情報が送られていない場合は、request.userに匿名ユーザーがセットされます。
アクセスの許可や拒否は、Permissionが担います。例えば、「ログインしたユーザーだけがデータを作成できるようにしたい」、「管理者ユーザーだけがデータを閲覧できるようにしたい」場面です。Permissionを設定することで、Authenticationで特定したユーザーにそのアクセスを許可するかどうかを判定します。
DRFにおけるPermissionの仕組み
DRFのPermissionは、permission_classesのリストとして定義されます。
from rest_framework.permissions import IsAuthenticated
from rest_framework.views import APIView
from rest_framework.response import Response
class ExampleView(APIView):
# リストとして定義されている
permission_classes = [IsAuthenticated]
def get(self, request, format=None):
return Response({'status': 'permitted'})
リクエストが来ると、Viewの本処理が実行される前に、リスト内が順番にチェックされます。いずれかのチェックが失敗した場合、Viewの処理は実行されません。
Permissionの設定方法
settings.pyのDEFAULT_PERMISSION_CLASSESを使うことで、すべてのViewに対してデフォルトの Permissionを一括で設定できます。
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
]
}
この設定の大きなメリットは、Permissionの設定忘れを防げることです。個別のViewにPermissionを設定し忘れても、グローバル設定が適用されるため、意図せず全員がアクセスできる状態になるリスクを下げられます。
特に指定しない場合、デフォルトはAllowAny(全員許可)になります。
View単位の設定
クラスベースViewでは、permission_classes属性を使ってView単位で Permissionを設定できます。Viewでpermission_classesを指定した場合は、そのViewではDEFAULT_PERMISSION_CLASSESは使われません。
from rest_framework.permissions import IsAuthenticated
from rest_framework.views import APIView
from rest_framework.response import Response
class ExampleView(APIView):
permission_classes = [IsAuthenticated]
def get(self, request, format=None):
return Response({'status': 'permitted'})
関数ベースViewでの設定
@api_viewデコレータを使った関数ベースViewでは、@permission_classesデコレータで設定します。
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
@api_view(['GET'])
@permission_classes([IsAuthenticated])
def example_view(request, format=None):
return Response({'status': 'permitted'})
組み込みPermission
DRFにはよく使われるPermissionクラスが用意されています。いくつかをご紹介します。
| クラス名 | 概要 |
|---|---|
AllowAny |
認証の有無に関わらず、すべてのリクエストを許可する |
IsAuthenticated |
認証済みユーザーのみ許可する |
IsAdminUser |
user.is_staffがTrueのユーザーのみ許可する |
IsAuthenticatedOrReadOnly |
認証済みユーザーはすべての操作を許可。未認証ユーザーはGET・HEAD・OPTIONSのみ許可 |
そのほかにも、組み込みPermissionが用意されているので、ぜひ公式ドキュメントをご確認ください。
Custom permissions
組み込みのPermissionでは対応できない場合、カスタムPermissionを作成します。BasePermissionを継承し、以下のメソッドを実装します。
-
has_permission(self, request, view)
Viewへのアクセス可否を判定する(リクエスト単位) -
has_object_permission(self, request, view, obj)
特定オブジェクトへのアクセス可否を判定する(オブジェクト単位)
どちらもTrueを返せば許可、Falseを返せば拒否です。
has_permission の実装例
from rest_framework import permissions
class IsAdminOrReadOnly(permissions.BasePermission):
"""
管理者のみ書き込みを許可し、それ以外は読み取りのみ許可する
"""
def has_permission(self, request, view):
# SAFE_METHODSはGET・HEAD・OPTIONSを表します
if request.method in permissions.SAFE_METHODS:
return True
return request.user.is_staff
Object level permissions について
Viewレベルのチェックを通過した後、さらに特定のオブジェクトに対してアクセスの可否を判断できます。例えば以下のような制御が可能です。
- 自分が作成した投稿のみ編集・削除を許可する
- 他のユーザーのプロフィールへの書き込みを拒否する
ただし、この制御はURLでオブジェクトが特定できる場合(GET /posts/5/など)にのみ適用されます。GET /posts/のような一覧取得では何十件ものPostがあります。DRFはどのPostに対して判定すればよいか分からないため、一覧取得ではObject level permissionsは呼ばれません。
設定方法に関しては長くなるため、ここでは省略します。
まとめ
Authenticationでユーザーを特定し、Permissionでアクセス権限を判定するという流れを理解すると、DRFの認証・認可の仕組みが理解しやすくなります。そして、DRFの Permission では、
- ユーザーのアクセス権限のチェック
- デフォルトの Permission 設定
- オブジェクト単位でのアクセス制御
が実現できます。公式ドキュメントを読み進める際や実装の際に、ご参考にしていただけると幸いです!