Django REST Framework(DRF)におけるアクセス制御の3つのコア機能——ユーザー認証、操作権限付与、およびリクエスト頻度制限——は、APIのセキュリティと信頼性を支える基盤です。これらは独立して適用可能であり、組み合わせることで細かいアクセス制御が実現できます。
実行フローの優先順位
DRFでは、各リクエストに対して以下の順序で処理が行われます:認証 → 権限チェック → レート制限。この順序は変更不可であり、たとえば権限判定は認証が成功した後にのみ実行されます。
カスタム認証クラスの構築
独自の認証ロジックを実装するには、rest_framework.authentication.BaseAuthentication を継承します。
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from myapp.models import ApiToken
class TokenBasedAuth(BaseAuthentication):
def authenticate(self, request):
auth_header = request.META.get('HTTP_AUTHORIZATION')
if not auth_header or not auth_header.startswith('Bearer '):
raise AuthenticationFailed('認証ヘッダーが不正です')
token_value = auth_header[7:] # 'Bearer ' を除去
try:
token = ApiToken.objects.select_related('user').get(value=token_value)
if not token.is_valid():
raise AuthenticationFailed('トークンの有効期限が切れています')
return token.user, token
except ApiToken.DoesNotExist:
raise AuthenticationFailed('無効なトークンです')
このクラスは、ビュークラスで個別に指定することも、settings.py でグローバルに設定することも可能です:
# views.py
from rest_framework.viewsets import ModelViewSet
class ProtectedResourceView(ModelViewSet):
queryset = Resource.objects.all()
serializer_class = ResourceSerializer
authentication_classes = [TokenBasedAuth] # 局所的適用
# settings.py
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'myapp.auth.TokenBasedAuth',
],
}
権限制御の実装
権限クラスは rest_framework.permissions.BasePermission を継承し、has_permission() メソッドで許可条件を定義します。
from rest_framework.permissions import BasePermission
class RoleBasedPermission(BasePermission):
def has_permission(self, request, view):
user = request.user
if not user.is_authenticated:
return False
# 管理者なら全操作許可
if user.is_staff:
return True
# 一般ユーザーはGETのみ許可(ただし特定アクションは除外)
if request.method == 'GET':
return True
# POST/PUT/DELETEは特定のビューでのみ許可
if view.action in ['update_profile', 'delete_draft']:
return user.has_perm('myapp.change_userprofile')
return False
権限クラスは、ビュークラスの permission_classes 属性で指定します。複数クラスを並べると、すべてが True を返す必要があります(AND条件)。
レート制限のカスタマイズ
DRFは SimpleRateThrottle を提供しており、IPアドレス単位での制限が簡単に実装できます。
from rest_framework.throttling import SimpleRateThrottle
class PerIPThrottle(SimpleRateThrottle):
scope = 'ip_limit'
rate = '10/h' # 1時間あたり最大10回
def get_cache_key(self, request, view):
return self.get_ident(request) # IPアドレスをキーとして使用
より高度な制御が必要な場合、BaseThrottle を直接継承し、allow_request() をオーバーライドします。以下は、Redisを用いた分散環境対応の例です:
import redis
from rest_framework.throttling import BaseThrottle
class RedisThrottle(BaseThrottle):
def __init__(self):
self.redis_client = redis.Redis(host='localhost', port=6379, db=1)
def allow_request(self, request, view):
client_id = self.get_ident(request)
key = f"throttle:{client_id}:{view.__class__.__name__}"
now = int(time.time())
# 5分間のウィンドウでカウンタを管理
window_start = now - 300
pipeline = self.redis_client.pipeline()
pipeline.zremrangebyscore(key, 0, window_start)
pipeline.zcard(key)
pipeline.zadd(key, {now: now})
pipeline.expire(key, 300)
_, count, _, _ = pipeline.execute()
return count <= 20 # 5分間に20回まで
このレート制限クラスは、ビューに直接適用できます:
class SensitiveActionView(APIView):
throttle_classes = [RedisThrottle]
def post(self, request):
# 敏感な操作(例:パスワード変更)
pass