解説は上記記事で構築した環境をもとに行いますので、プロジェクトやアプリケーションのディレクトリ、docker関連のコマンドなどは適宜読み替えてください。
プロジェクト ... /project
アプリケーション ... /app
環境
OS ... macOS Ventura 13.1
エディタ ... VSCode
ライブラリ
Django==4.1.7
graphene_django==3.0.0
django-graphql-jwt==0.3.4
django-cors-headers==3.14.0
セットアップ
settings.pyに設定情報を追加
INSTALLED_APPS = [
'corsheaders',
'graphql_jwt.refresh_token.apps.RefreshTokenConfig',
]
MIDDLEWARE = [
"corsheaders.middleware.CorsMiddleware",
]
AUTHENTICATION_BACKENDS = [
'graphql_jwt.backends.JSONWebTokenBackend',
'django.contrib.auth.backends.ModelBackend',
]
GRAPHENE = {
'SCHEMA': 'project.schema.schema',
'MIDDLEWARE': [
'graphql_jwt.middleware.JSONWebTokenMiddleware',
],
}
GRAPHQL_JWT = {
'JWT_VERIFY_EXPIRATION': True, # tokenの有効期限を確認する
'JWT_LONG_RUNNING_REFRESH_TOKEN': True, # リフレッシュトークンを有効化
'JWT_EXPIRATION_DELTA': timedelta(minutes=10), # tokenの有効期限
'JWT_REFRESH_EXPIRATION_DELTA': timedelta(days=7), refreshTokenの有効期限
}
CORS_ORIGIN_WHITELIST = [
"http://localhost:8000", CORSでアクセスを許可するURL
]
追加後にpython manage.py migrate
を実行してください。
タイプを定義
appのtypes.pyに、ユーザーに関するタイプを追加します。
from graphene_django.types import DjangoObjectType
from django.contrib.auth import get_user_model
class UserType(DjangoObjectType):
class Meta:
model = get_user_model()
スキーマを定義
appのscheme.pyに、認証に関する処理を追加します。
import graphene
from app.models import Fruit
from .types import UserType, FruitType
from django.contrib.auth import get_user_model
from graphql_jwt.decorators import login_required
import graphql_jwt
User = get_user_model()
class Query:
current_user = graphene.Field(UserType)
all_users = graphene.List(UserType)
def resolve_current_user(root, info):
user = info.context.user
return user
# ログインしている場合のみ呼び出し可能
@login_required
def resolve_all_users(root, info):
return User.objects.all()
class CreateUserMutation(graphene.Mutation):
success = graphene.Boolean()
message = graphene.String()
user = graphene.Field(UserType)
class Arguments:
username = graphene.String(required=True)
email = graphene.String(required=True)
password1 = graphene.String(required=True)
password2 = graphene.String(required=True)
def mutate(self, info, username, email, password1, password2):
existsUsername = User.objects.filter(username=username).exists()
if existsUsername:
return CreateUserMutation(success=False, message="登録済みのユーザーIDです", user=None)
existsEmail = User.objects.filter(email=email).exists()
if existsEmail:
return CreateUserMutation(success=False, message="登録済みのメールアドレスです", user=None)
errorPassword = password1 != password2
if errorPassword:
return CreateUserMutation(success=False, message="パスワードが一致していません", user=None)
user = get_user_model()(
username=username,
email=email,
)
user.set_password(password1)
user.save()
return CreateUserMutation(success=True, message="アカウント登録に成功しました", user=user)
class Mutation(graphene.ObjectType):
create_user = CreateUserMutation.Field() # ユーザー登録
token_auth = graphql_jwt.ObtainJSONWebToken.Field() # トークン生成
verify_token = graphql_jwt.Verify.Field() # トークン認証
refresh_token = graphql_jwt.Refresh.Field() # トークン再生成
revoke_token = graphql_jwt.Revoke.Field() # リフレッシュトークン無効化
エンドポイントの追加
GraphQLのエンドポイントを追加します。
今回はAltairからアクセスを行いたいため、csrf_exempt
を用いてCSRFのエラーが発生しないようにします。
from django.urls import path
from graphene_django.views import GraphQLView
from project.scheme import schema
from django.views.decorators.csrf import csrf_exempt
urlpatterns = [
path('graphql/', csrf_exempt(GraphQLView.as_view(graphiql=True, schema=schema))),
]
動作確認
ここまでで設定は完了したので、動作確認を行います。
アカウント作成
mutation {
createUser(
username: "sample"
email: "sample@gmail.com"
password1: "sample1234"
password2: "sample1234"
) {
success
message
user {
id
username
email
}
}
}
登録が成功すると、登録したユーザーの情報がレスポンスされます。
登録済みの情報やパスワードが一致していない場合、エラーが返却されます。
トークン生成
mutation {
tokenAuth(username: "sample", password: "sample1234") {
payload
refreshExpiresIn
token
refreshToken
}
}
先程登録したアカウントを用いてトークン生成を行います。
レスポンスされたtokenをヘッダーにセットしてリクエストを行うことで、サーバに認証済みのアカウントからのリクエストであると認識されます。
ヘッダー情報
- Key ... Authorization
- value ... JWT [token] (例: tokenが 1q2w3e4r の場合 JWT 1q2w3e4r)
現在のユーザーを取得
query {
currentUser {
id
username
email
}
}
ヘッダーに先程取得したtokenをセットしてリクエストすると、ユーザー情報が返却されます。
tokenがセットされていなかったり間違っていたりすると、エラーが返却されます。
ユーザー一覧を取得する
query {
allUsers {
id
username
email
}
}
allUsersは @login_required
デコレーターがついているため、ヘッダーにtokenがセットされている場合ユーザー一覧がレスポンスされます。
トークン認証
mutation {
verifyToken(token: [token]) {
payload
}
}
トークン再生成
mutation {
refreshToken(refreshToken: [refreshToken]) {
payload
refreshExpiresIn
token
refreshToken
}
}
リフレッシュトークン無効化
mutation {
revokeToken(refreshToken: [refreshToken]) {
revoked
}
}