🍣

# 6.4 ログイン API の実装

に公開

前回で accounts アプリを作り、ユーザ・部署・ロールを管理できる環境が整った。
今回は Django REST Framework(DRF)を使って ログイン API を実装していく。

ここでは モデルと同じくファイルを分割する構成を採用する。
規模が大きくなっても見通しよく管理できるため、業務システムに向いている。


1. ディレクトリ構成

accounts/
  models/
    __init__.py
    user.py
    dept.py
    role.py
    user_dept.py
    user_role.py
  serializers/
    __init__.py
    auth.py
  views/
    __init__.py
    auth.py
  urls.py
  admin.py

👉 モデルだけでなく、シリアライザ・ビューも機能単位で分割する。


2. DRF の設定

settings.py に追加:

INSTALLED_APPS = [
    ...,
    "rest_framework",
    "corsheaders",
    "accounts",
]

MIDDLEWARE = [
    "corsheaders.middleware.CorsMiddleware",
    "django.middleware.common.CommonMiddleware",
    ...
]

REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": [
        "rest_framework.authentication.SessionAuthentication",  # セッション認証
    ],
    "DEFAULT_PERMISSION_CLASSES": [
        "rest_framework.permissions.IsAuthenticated",
    ],
}

👉 今回は セッション認証 を採用。
Vue からのリクエストで Cookie を使ってログイン状態を保持する。


3. シリアライザ

accounts/serializers/auth.py

from rest_framework import serializers

class LoginSerializer(serializers.Serializer):
    username = serializers.CharField()
    password = serializers.CharField()

👉 ユーザ名とパスワードを受け取るだけのシンプルなシリアライザ。


4. ビュー

accounts/views/auth.py

from django.contrib.auth import authenticate, login, logout
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from ..serializers.auth import LoginSerializer
from ..models import UserDept, UserRole

class LoginView(APIView):
    permission_classes = []  # 未ログインでも使える

    def post(self, request):
        serializer = LoginSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        user = authenticate(
            request,
            username=serializer.validated_data["username"],
            password=serializer.validated_data["password"],
        )
        if user is not None:
            login(request, user)
            return Response({"message": "ログイン成功"})
        return Response({"error": "認証失敗"}, status=status.HTTP_401_UNAUTHORIZED)


class LogoutView(APIView):
    def post(self, request):
        logout(request)
        return Response({"message": "ログアウトしました"})


class AuthStatusView(APIView):
    def get(self, request):
        if request.user.is_authenticated:
            departments = [
                {"code": ud.dept.code, "name": ud.dept.name}
                for ud in UserDept.objects.filter(user=request.user)
            ]
            roles = [ur.role.code for ur in UserRole.objects.filter(user=request.user)]
            return Response({
                "username": request.user.username,
                "full_name": request.user.full_name,
                "is_staff": request.user.is_staff,
                "departments": departments,
                "roles": roles,
            })
        return Response({"authenticated": False}, status=status.HTTP_401_UNAUTHORIZED)

👉

  • LoginView … 認証成功ならセッションを開始
  • LogoutView … セッションを破棄
  • AuthStatusView … 現在ログインしているユーザの部署・ロールを返す

5. URL 設定

accounts/urls.py

from django.urls import path
from .views.auth import LoginView, LogoutView, AuthStatusView

urlpatterns = [
    path("auth/login/", LoginView.as_view(), name="login"),
    path("auth/logout/", LogoutView.as_view(), name="logout"),
    path("auth/status/", AuthStatusView.as_view(), name="auth_status"),
]

project/urls.py

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path("admin/", admin.site.urls),
    path("api/", include("accounts.urls")),
]

6. 動作確認

6.1 ログイン

curl -X POST -H "Content-Type: application/json" -d "{\"username\":\"t.yamada\",\"password\":\"pass123\"}" -c cookies.txt http://127.0.0.1:8000/api/auth/login/

6.2 認証確認

curl -X GET -b cookies.txt http://127.0.0.1:8000/api/auth/status/

レスポンス例:

{
  "username": "taro",
  "full_name": "山田 太郎",
  "is_staff": true,
  "departments": [
    {"code": "A001", "name": "営業一課"}
  ],
  "roles": ["order_approve"]
}

今回のゴール

  • DRF で ログイン/ログアウト/認証確認 API を実装
  • ディレクトリを分けて models / serializers / views を整理
  • 認証済みユーザの部署・ロール情報を返せるようにした
  • Vue 側からセッションを保持して API を叩ける準備が整った

次の展開

次は 6.5 Vue との接続編
Vue 側に 共通 axios クライアント を用意し、今回のログイン API と認証状態を実際に呼び出してみる。

Discussion