تمتع بمزيد من التحكم في منطق مصادقة تطبيق Next.js الخاص بك من خلال تنفيذ المصادقة المخصصة المستندة إلى JWT.

تعد مصادقة الرمز المميز إستراتيجية شائعة تستخدم في حماية تطبيقات الويب والهاتف المحمول من الوصول غير المصرح به. في Next.js، يمكنك الاستفادة من ميزات المصادقة التي توفرها Next-auth.

وبدلاً من ذلك، يمكنك اختيار تطوير نظام مصادقة مخصص قائم على الرمز المميز باستخدام JSON Web Tokens (JWTs). ومن خلال القيام بذلك، فإنك تضمن أن لديك المزيد من التحكم في منطق المصادقة؛ بشكل أساسي، تخصيص النظام ليتوافق بدقة مع متطلبات مشروعك.

قم بإعداد مشروع Next.js

للبدء، قم بتثبيت Next.js عن طريق تشغيل الأمر أدناه على جهازك.

npx create-next-app@latest next-auth-jwt --experimental-app

سوف يستخدم هذا الدليل Next.js 13 الذي يتضمن دليل التطبيق.

بعد ذلك، قم بتثبيت هذه التبعيات في مشروعك باستخدام npm، مدير حزم العقدة.

npm install jose universal-cookie

خوسيه عبارة عن وحدة JavaScript توفر مجموعة من الأدوات المساعدة للعمل مع JSON Web Tokens أثناء ملف تعريف الارتباط العالمي توفر التبعية طريقة بسيطة للعمل مع ملفات تعريف الارتباط للمتصفح في كل من البيئات من جانب العميل والخادم.

instagram viewer

يمكنك العثور على رمز هذا المشروع في هذا مستودع جيثب.

إنشاء واجهة مستخدم نموذج تسجيل الدخول

افتح ال src/app الدليل، قم بإنشاء مجلد جديد، وقم بتسميته تسجيل الدخول. داخل هذا المجلد، أضف ملفًا جديدًا page.js الملف وقم بتضمين الكود أدناه.

"use client";
import { useRouter } from"next/navigation";

exportdefaultfunctionLoginPage() {
return (


ينشئ الكود أعلاه مكونًا وظيفيًا لصفحة تسجيل الدخول والذي سيعرض نموذج تسجيل دخول بسيط على المتصفح للسماح للمستخدمين بإدخال اسم مستخدم وكلمة مرور.

ال استخدام العميل يضمن بيان في التعليمات البرمجية أن يتم إعلان الحدود بين التعليمات البرمجية للخادم فقط والعميل فقط في برنامج الدليل.

في هذه الحالة، يتم استخدامه للإعلان عن أن الرمز الموجود في صفحة تسجيل الدخول، وخاصةً رمز HandleSubmitيتم تنفيذ الوظيفة فقط على العميل؛ وإلا، فسيقوم Next.js بإلقاء خطأ.

الآن دعونا نحدد الكود الخاص بالـ HandleSubmit وظيفة. داخل المكون الوظيفي، أضف الكود التالي.

const router = useRouter();

const handleSubmit = async (event) => {
event.preventDefault();
const formData = new FormData(event.target);
const username = formData.get("username");
const password = formData.get("password");
const res = await fetch("/api/login", {
method: "POST",
body: JSON.stringify({ username, password }),
});
const { success } = await res.json();
if (success) {
router.push("/protected");
router.refresh();
} else {
alert("Login failed");
}
 };

لإدارة منطق مصادقة تسجيل الدخول، تلتقط هذه الوظيفة بيانات اعتماد المستخدم من نموذج تسجيل الدخول. ثم يرسل بعد ذلك طلب POST إلى نقطة نهاية API لتمرير تفاصيل المستخدم للتحقق منها.

إذا كانت بيانات الاعتماد صالحة، فهذا يشير إلى أن عملية تسجيل الدخول كانت ناجحة - تُرجع واجهة برمجة التطبيقات حالة النجاح في الاستجابة. ستستخدم وظيفة المعالج بعد ذلك جهاز توجيه Next.js لتنقل المستخدم إلى عنوان URL محدد، في هذه الحالة، عنوان URL محمي طريق.

تحديد نقطة نهاية واجهة برمجة تطبيقات تسجيل الدخول

داخل src/app الدليل، قم بإنشاء مجلد جديد وقم بتسميته واجهة برمجة التطبيقات. داخل هذا المجلد، أضف ملفًا جديدًا تسجيل الدخول/route.js الملف وقم بتضمين الكود أدناه.

import { SignJWT } from"jose";
import { NextResponse } from"next/server";
import { getJwtSecretKey } from"@/libs/auth";

exportasyncfunctionPOST(request) {
const body = await request.json();
if (body.username "admin" && body.password "admin") {
const token = awaitnew SignJWT({
username: body.username,
})
.setProtectedHeader({ alg: "HS256" })
.setIssuedAt()
.setExpirationTime("30s")
.sign(getJwtSecretKey());
const response = NextResponse.json(
{ success: true },
{ status: 200, headers: { "content-type": "application/json" } }
);
response.cookies.set({
name: "token",
value: token,
path: "/",
});
return response;
}
return NextResponse.json({ success: false });
}

تتمثل المهمة الأساسية لواجهة برمجة التطبيقات هذه في التحقق من بيانات اعتماد تسجيل الدخول التي تم تمريرها في طلبات POST باستخدام بيانات وهمية.

عند التحقق الناجح، يقوم بإنشاء رمز JWT مشفر مرتبط بتفاصيل المستخدم المصادق عليها. وأخيرًا، يرسل استجابة ناجحة إلى العميل، بما في ذلك الرمز المميز في ملفات تعريف الارتباط الخاصة بالاستجابة؛ وإلا فإنه يقوم بإرجاع استجابة حالة الفشل.

تنفيذ منطق التحقق من الرمز المميز

الخطوة الأولى في مصادقة الرمز المميز هي إنشاء الرمز المميز بعد عملية تسجيل دخول ناجحة. الخطوة التالية هي تنفيذ منطق التحقق من الرمز المميز.

في الأساس، سوف تستخدم jwtVerify الوظيفة التي تقدمها خوسيه وحدة للتحقق من رموز JWT التي تم تمريرها مع طلبات HTTP اللاحقة.

في ال src الدليل، إنشاء جديد ليب/auth.js الملف وقم بتضمين الكود أدناه.

import { jwtVerify } from"jose";

exportfunctiongetJwtSecretKey() {
const secret = process.env.NEXT_PUBLIC_JWT_SECRET_KEY;
if (!secret) {
thrownewError("JWT Secret key is not matched");
}
returnnew TextEncoder().encode(secret);
}

exportasyncfunctionverifyJwtToken(token) {
try {
const { payload } = await jwtVerify(token, getJwtSecretKey());
return payload;
} catch (error) {
returnnull;
}
}

يتم استخدام المفتاح السري في التوقيع والتحقق من الرموز المميزة. من خلال مقارنة توقيع الرمز المميز الذي تم فك تشفيره بالتوقيع المتوقع، يمكن للخادم التحقق بشكل فعال من صلاحية الرمز المميز المقدم، وفي النهاية، السماح بطلبات المستخدمين.

يخلق .env الملف في الدليل الجذر وأضف مفتاحًا سريًا فريدًا كما يلي:

NEXT_PUBLIC_JWT_SECRET_KEY=your_secret_key

إنشاء طريق محمي

أنت الآن بحاجة إلى إنشاء مسار يمكن للمستخدمين المصادق عليهم فقط الوصول إليه. للقيام بذلك، قم بإنشاء ملف جديد محمي/page.js الملف في src/app الدليل. داخل هذا الملف قم بإضافة الكود التالي.

exportdefaultfunctionProtectedPage() {
return<h1>Very protected pageh1>;
}

قم بإنشاء خطاف لإدارة حالة المصادقة

قم بإنشاء مجلد جديد في src الدليل وتسميته خطافات. داخل هذا المجلد أضف ملفًا جديدًا استخدامAuth/index.js الملف وقم بتضمين الكود أدناه.

"use client" ;
import React from"react";
import Cookies from"universal-cookie";
import { verifyJwtToken } from"@/libs/auth";

exportfunctionuseAuth() {
const [auth, setAuth] = React.useState(null);

const getVerifiedtoken = async () => {
const cookies = new Cookies();
const token = cookies.get("token")?? null;
const verifiedToken = await verifyJwtToken(token);
setAuth(verifiedToken);
};
React.useEffect(() => {
getVerifiedtoken();
}, []);
return auth;
}

يدير هذا الخطاف حالة المصادقة من جانب العميل. يقوم بجلب والتحقق من صلاحية رمز JWT الموجود في ملفات تعريف الارتباط باستخدام ملف checkJwtToken الوظيفة، ثم يقوم بتعيين تفاصيل المستخدم المصادق عليها إلى مصادقة ولاية.

ومن خلال القيام بذلك، فإنه يسمح للمكونات الأخرى بالوصول إلى معلومات المستخدم المصادق عليها واستخدامها. يعد هذا ضروريًا لسيناريوهات مثل إجراء تحديثات واجهة المستخدم بناءً على حالة المصادقة، أو تقديم طلبات واجهة برمجة التطبيقات اللاحقة، أو عرض محتوى مختلف بناءً على أدوار المستخدم.

في هذه الحالة، ستستخدم الخطاف لعرض محتوى مختلف على الملف بيت الطريق بناءً على حالة المصادقة الخاصة بالمستخدم.

النهج البديل الذي قد تفكر فيه هو التعامل إدارة الحالة باستخدام Redux Toolkit أو توظيف أ أداة إدارة الحالة مثل Jotai. يضمن هذا الأسلوب حصول المكونات على وصول عالمي إلى حالة المصادقة أو أي حالة أخرى محددة.

المضي قدما وفتح التطبيق/page.js الملف، وحذف التعليمات البرمجية النمطية Next.js، وإضافة التعليمات البرمجية التالية.

"use client" ;

import { useAuth } from"@/hooks/useAuth";
import Link from"next/link";
exportdefaultfunctionHome() {
const auth = useAuth();
return<>

Public Home Page</h1>

يستخدم الكود أعلاه useAuth ربط لإدارة حالة المصادقة. ومن خلال القيام بذلك، فإنه يعرض بشكل مشروط صفحة رئيسية عامة تحتوي على رابط إلى تسجيل الدخول مسار الصفحة عندما لا تتم مصادقة المستخدم، ويعرض فقرة لمستخدم تمت مصادقته.

إضافة برنامج وسيط لفرض الوصول المصرح به إلى المسارات المحمية

في ال src الدليل، إنشاء جديد middleware.js الملف، وأضف الكود أدناه.

import { NextResponse } from"next/server";
import { verifyJwtToken } from"@/libs/auth";

const AUTH_PAGES = ["/login"];

const isAuthPages = (url) => AUTH_PAGES.some((page) => page.startsWith(url));

exportasyncfunctionmiddleware(request) {

const { url, nextUrl, cookies } = request;
const { value: token } = cookies.get("token")?? { value: null };
const hasVerifiedToken = token && (await verifyJwtToken(token));
const isAuthPageRequested = isAuthPages(nextUrl.pathname);

if (isAuthPageRequested) {
if (!hasVerifiedToken) {
const response = NextResponse.next();
response.cookies.delete("token");
return response;
}
const response = NextResponse.redirect(new URL(`/`, url));
return response;
}

if (!hasVerifiedToken) {
const searchParams = new URLSearchParams(nextUrl.searchParams);
searchParams.set("next", nextUrl.pathname);
const response = NextResponse.redirect(
new URL(`/login?${searchParams}`, url)
);
response.cookies.delete("token");
return response;
}

return NextResponse.next();

}
exportconst config = { matcher: ["/login", "/protected/:path*"] };

يعمل رمز البرنامج الوسيط هذا كحارس. فهو يتحقق للتأكد من أنه عندما يرغب المستخدمون في الوصول إلى الصفحات المحمية، يتم مصادقتهم ومصرح لهم بالوصول إلى المسارات، بالإضافة إلى إعادة توجيه المستخدمين غير المصرح لهم إلى صفحة تسجيل الدخول.

تأمين تطبيقات Next.js

تعد مصادقة الرمز المميز آلية أمان فعالة. ومع ذلك، فهي ليست الإستراتيجية الوحيدة المتاحة لحماية تطبيقاتك من الوصول غير المصرح به.

لتحصين التطبيقات في مواجهة المشهد الديناميكي للأمن السيبراني، من المهم اعتماد أمان شامل النهج الذي يعالج بشكل شامل الثغرات الأمنية ونقاط الضعف المحتملة لضمان شامل حماية.