LoginSignup
1
0

More than 1 year has passed since last update.

Django REST Frameworkで自作クラスに紐づけた API Key によるアクセス制限を行う

Posted at

この記事は何?

Django で Django REST Framework によるAPI開発を行う際、ユーザークラスではなく自作のクラス(デバイスなど)に紐づけたAPI Keyを発行し、APIで認証を行う方法のメモです。

環境

  • django 4.2
  • djangorestframework 3.14.0
  • djangorestframework-api-key 2.3.0

やりたいこと

Django WEBアプリ上で端末情報を登録し、APIへのアクセスを登録された端末からのみに制限する、ということをしたいです。

ここでは、Django REST framework を使用してAPIを実装し、Django REST Framework API Key を使用してAPIキーによるアクセス制限を行います。

Django REST framework にはトークン認証を行う TokenAuthentication が標準で含まれていますが、こちらは DjangoのUserクラス に紐づいてキーが発行されるようなので、今回は使用していません。

実装例

model.py

端末を表す Device クラス、端末に紐づける DeviceAPIKey クラス、アクセス権限を与えるための HasDeviceAPIKey クラスを定義します。

from django.db import models
from rest_framework_api_key.models import AbstractAPIKey
from rest_framework_api_key.permissions import BaseHasAPIKey

class Device(models.Model):
    """登録するデバイス情報"""

    id = models.AutoField("ID", primary_key=True)
    name = models.CharField("デバイス名", max_length=50, )
    created_at = models.DateTimeField("作成日時", auto_now_add=True)
    updated_at = models.DateTimeField("更新日時", auto_now=True)

    def __str__(self):
        return f"{self.name}"


class DeviceAPIKey(AbstractAPIKey):
    """デバイスに紐づけるAPIキークラス"""
    device = models.ForeignKey(
        Device,
        on_delete=models.CASCADE,
        related_name="api_keys",
    )


class HasDeviceAPIKey(BaseHasAPIKey):
    """認証用クラス"""
    model = DeviceAPIKey

キー発行view例

デバイス登録時にAPIキーを発行し、画面に表示します。ユーザーは画面に表示されたキー情報を保存しておき、アクセス時に使用します。

def device_create(request):
    if request.method == "POST":
        device_form = DeviceForm(request.POST)
        if device_form.is_valid():
            device = device_form.save(commit=False)
            # Deviceモデルに対してAPIキーを発行,紐づけ
            _, api_key = DeviceAPIKey.objects.create_key(name=device.id, device=device)
            context = {"api_key": api_key}
            return render(request, "Devices/device_create_complete.html", context=context) #デバイス登録完了画面で デバイスに対して発行した API Key を表示する.
    else:
        device_form = DeviceForm()

    return render(request, "Devices/device_create.html") #デバイス登録画面表示

API View

permission_classes に HasDeviceAPIKey を指定することで、発行されたAPIキーがリクエストヘッダーに含まれていない場合、アクセスを拒否することができます。
また、ヘッダに含まれるキー情報から, キーに紐づくデバイスを取得することもできます。

from devices.models import Device, DeviceAPIKey HasDeviceAPIKey
from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView

class DeviceAPIView(APIView):
    permission_classes = [HasDeviceAPIKey] # DeviceAPIKey で発行したトークンがHeadersに含まれる場合のみアクセスを許可

    def get(self, request):
        # key に紐づくdeviceを取得
        api_key = request.META.get("HTTP_X_API_KEY")
        key = DeviceAPIKey.objects.get_from_key(api_key)
        device = Device.objects.get(api_keys=key)
        print(f"device {device.name} conntected.")
        return Response({"message": "Success"}, status=status.HTTP_200_OK)

settings.py

以下を追記し、リクエスト時のヘッダー名を X-Api-Key に変更しています。

# header key名を X-Api-Keyに変更
API_KEY_CUSTOM_HEADER = "HTTP_X_API_KEY"

リクエスト

登録後に表示された APIkey をリクエストヘッダーに含めて送信します。
APIKeyが不正な場合、アクセスが拒否され403レスポンスが返却されます。

url = "api_url"
api_key="your key"
headers = {"X-Api-Key": api_key}
response = requests.get(url, headers=headers)

その他

まとめ

ユーザー以外のモデルと紐づけてAPIのアクセス制限を行う方法を記載しました。
より高度な認証などが必要な場合は、この方法では不十分なので別の方法が必要です。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0