Nextjs 및 Django의 jwt 제작 인증 기능
원래 쿠키에 넣은 jwt를 httpOnly로 바꾸려고 했는데, 단순 jwt의 규격상 쿠키가 필요한 부분은 스스로 만들어야 한다.(simplejwt의 원본 코드를 완전히 읽지 않아서 잘 모르겠습니다.)
그래서 머리를 쥐어짜서 약간 단순 jwt를 만들어서 실현했다.
아직 어른스럽지는 않지만 좋은 걸 만들 수 있을 것 같아서 기사를 썼어요.
참고문과 전제
Django
간단하게 DRF(Django rest frame work)를 사용합니다.
jwt
이분처럼 직접 만든 것도 있지만 귀찮아서 단순 jwt를 썼어요.
Nextjs
전 세계 로그인 상태의 관리캣노스 씨.에 관하여 나는 이 글을 완전히 참고하였다.
구조
로그인 처리
사용자 이름과 비밀번호로 로그인한 후 jwt의access token과refresh token을 쿠키로 설정합니다.
부합catnose의 보도fetch 현재 User의 처리는 다음과 같다.
최초 DOM 구축 시 쿠키의access token을 이용하여 사용자의 정보를 얻고state에 넣습니다.
access token이 끊어진 상태에서refresh token을 던져access token을 획득합니다.access token을 통해 사용자 정보를 다시 시도합니다.
이루어지다
쿠키에 jwt 처리 설정
단순 jwt의views.TokenObtainPairView를 살짝 맞춤형으로 만들어볼게요.
Token Obtain PairView는 POST의 사용자 이름과 비밀번호가 일치하면access token과refresh token을 반환합니다.이번에는 쿠키에 설치된 처리를 추가했습니다.secure 속성은middleware를 분리해서 제작하여 임의로 secure하는 것입니다.
# Django3
from rest_framework_simplejwt import views as jwt_views
from rest_framework_simplejwt import exceptions as jwt_exp
class TokenObtainView(jwt_views.TokenObtainPairView):
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
try:
serializer.is_valid(raise_exception=True)
except jwt_exp.TokenError as e:
raise jwt_exp.InvalidToken(e.args[0])
res = response.Response(serializer.validated_data, status=status.HTTP_200_OK)
try:
res.delete_cookie("user_token")
except Exception as e:
print(e) # ここら辺適当すぎる
# httpOnlyなのでtokenの操作は全てdjangoで行う
res.set_cookie(
"user_token",
serializer.validated_data["access"],
max_age=60 * 60 * 24,
httponly=True,
)
res.set_cookie(
"refresh_token",
serializer.validated_data["refresh"],
max_age=60 * 60 * 24 * 30,
httponly=True,
)
return res
access token에서user 정보 가져오기
쿠키에서access token을 주우고python의 jwt decode를 사용하는 곳이 바로 맛입니다
# Django3
import jwt
class UserAPIView(views.APIView):
def get_object(self, JWT):
try:
payload = jwt.decode(
jwt=JWT, key=settings.SECRET_KEY, algorithms=["HS256"]
)
# DBにアクセスせずuser_idだけの方がjwtの強みが生きるかも
# その場合 return payload["user_id"]
return User.objects.get(id=payload["user_id"])
except jwt.ExpiredSignatureError:
# access tokenの期限切れ
return "Activations link expired"
except jwt.exceptions.DecodeError:
return "Invalid Token"
except User.DoesNotExist:
return "user does not exists"
def get(self, request, format=None):
JWT = request.COOKIES.get("user_token")
if not JWT:
return response.Response(
{"error": "No token"}, status=status.HTTP_400_BAD_REQUEST
)
user = self.get_object(JWT)
# エラーならstringで帰ってくるので、型で判定
# ここイケてないな
if type(user) == str:
return response.Response(
{"error": user}, status=status.HTTP_400_BAD_REQUEST
)
if user.is_active:
serializer = UserSerializer(user)
return response.Response(serializer.data)
return response.Response(
{"error": "user is not active"}, status=status.HTTP_400_BAD_REQUEST
)
Nextjs 측 처리
access token을 통해 사용자 가져오기 시도
만약accesstoken이 만료된 오류라면refreshtoken으로accesstoken을 리셋하고 위의 처리를 다시 실행할 수 있습니다.
// Nextjs
export const fetchCurrentUser = async () => {
try {
const user = await tokenToUser();
return user;
} catch (e) {
// tokenの有効期限が切れていたら refreshを試みる
if (e["error"] === "Activations link expired") {
const refresh = await refreshToken();
const refreshRet = await newToken(refresh);
if (refreshRet["access"]) {
// refresh に成功したら再度 access tokenでのユーザー取得を試みる
const user = await tokenToUser();
return user;
}
}
}
};
// tokenからuser情報を取得
const tokenToUser = async () => {
const res = await fetch(`${baseUrl}/api/user/`, {
credentials: "include",
});
const ret = await res.json();
if (res.status === 400) {
throw ret;
}
return ret;
};
// refresh tokenをもらう
export const getRefreshToken = async () => {
const res = await fetch(`${baseUrl}/api/user/refresh/`, {
credentials: "include",
});
const ret = await res.json();
return ret;
};
// refresh tokenから 新しい access tokenを生成
const newToken = async (refresh: any) => {
const res = await fetch(`${baseUrl}/api/user/refresh/token/`, {
method: "POST",
credentials: "include",
headers: {
"Content-Type": "application/json; charset=utf-8",
"X-CSRFToken": csrf["token"],
},
body: JSON.stringify(refresh),
});
const ret = await res.json();
destroyCookie(null, "csrftoken");
return ret;
};
refresh 처리
refresh_get으로 refresh token을 취득하고 단순 jwt의 Token RefreshView에서 POST를 진행한다.
이렇게 하면 새로운 access token이 쿠키에 들어가요.
# Django3
def refresh_get(request):
try:
RT = request.COOKIES["refresh_token"]
return JsonResponse({"refresh": RT}, safe=False)
except Exception as e:
print(e)
return None
class TokenRefresh(jwt_views.TokenRefreshView):
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
try:
serializer.is_valid(raise_exception=True)
except jwt_exp.TokenError as e:
raise jwt_exp.InvalidToken(e.args[0])
res = response.Response(serializer.validated_data, status=status.HTTP_200_OK)
res.delete_cookie("user_token")
res.set_cookie(
"user_token",
serializer.validated_data["access"],
max_age=60 * 24 * 24 * 30,
httponly=True,
)
return res
nginx의cors를 설정한 후refreshget에서 Token Refresh requests로 직접 이동합니다.포스터도 있고.이상은 실제 처리입니다.
csrf 대책과 쿠키를 넣기 위한 middleware 등의 제작도 이번 실상에서 진행됐지만, 주제에서 벗어나 사랑을 끊었다.
잡담
당신의 질문과 의견을 기다리겠습니다.
핑계가 되지만 실제 업무가 1.5개월도 안 되는 엔지니어, 그리고 직장에서python과django를 사용하지 않아 난폭한 부분이 있으니 의견이 있으면 마음대로 논평해 주세요.
문제도 마음대로 논평해 주세요.
이 사이트에서 이 처리를 사용합니다.
관심 있으신 분들은 꼭 가보세요.
Reference
이 문제에 관하여(Nextjs 및 Django의 jwt 제작 인증 기능), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://zenn.dev/maru44/articles/8c22bbd6954ea6텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)