Erhalten Sie mehr Kontrolle über die Authentifizierungslogik Ihrer Next.js-App durch eine benutzerdefinierte JWT-basierte Authentifizierungsimplementierung.

Die Token-Authentifizierung ist eine beliebte Strategie zum Schutz von Web- und Mobilanwendungen vor unbefugtem Zugriff. In Next.js können Sie die von Next-auth bereitgestellten Authentifizierungsfunktionen nutzen.

Alternativ können Sie sich für die Entwicklung eines benutzerdefinierten tokenbasierten Authentifizierungssystems mithilfe von JSON Web Tokens (JWTs) entscheiden. Dadurch stellen Sie sicher, dass Sie mehr Kontrolle über die Authentifizierungslogik haben; Im Wesentlichen geht es darum, das System genau an die Anforderungen Ihres Projekts anzupassen.

Richten Sie ein Next.js-Projekt ein

Installieren Sie zunächst Next.js, indem Sie den folgenden Befehl auf Ihrem Terminal ausführen.

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

Dieser Leitfaden wird verwendet Next.js 13, das das App-Verzeichnis enthält.

Als nächstes installieren Sie diese Abhängigkeiten in Ihrem Projekt mit npm, der Node Package Manager.

npm install jose universal-cookie

José ist ein JavaScript-Modul, das eine Reihe von Dienstprogrammen für die Arbeit mit JSON-Web-Tokens bereitstellt Universal-Cookie Die Abhängigkeit bietet eine einfache Möglichkeit, mit Browser-Cookies sowohl in clientseitigen als auch serverseitigen Umgebungen zu arbeiten.

Den Code dieses Projekts finden Sie hier GitHub-Repository.

Erstellen Sie die Benutzeroberfläche für das Anmeldeformular

Öffne das src/app Verzeichnis, erstellen Sie einen neuen Ordner und benennen Sie ihn Anmeldung. Fügen Sie in diesem Ordner einen neuen hinzu page.js Datei und fügen Sie den folgenden Code ein.

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

exportdefaultfunctionLoginPage() {
return (


Der obige Code erstellt eine Funktionskomponente für die Anmeldeseite, die ein einfaches Anmeldeformular im Browser darstellt, damit Benutzer einen Benutzernamen und ein Kennwort eingeben können.

Der Client verwenden Die Anweisung im Code stellt sicher, dass eine Grenze zwischen Nur-Server- und Nur-Client-Code im angegeben wird App Verzeichnis.

In diesem Fall wird damit erklärt, dass der Code auf der Anmeldeseite, insbesondere der handleSubmitFunktion wird nur auf dem Client ausgeführt; Andernfalls gibt Next.js einen Fehler aus.

Definieren wir nun den Code für handleSubmit Funktion. Fügen Sie innerhalb der Funktionskomponente den folgenden Code hinzu.

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");
}
 };

Um die Anmeldeauthentifizierungslogik zu verwalten, erfasst diese Funktion die Benutzeranmeldeinformationen aus dem Anmeldeformular. Anschließend sendet es eine POST-Anfrage an einen API-Endpunkt und übermittelt die Benutzerdetails zur Überprüfung.

Wenn die Anmeldeinformationen gültig sind, zeigt dies an, dass der Anmeldevorgang erfolgreich war – die API gibt in der Antwort einen Erfolgsstatus zurück. Die Handlerfunktion verwendet dann den Router von Next.js, um den Benutzer zu einer angegebenen URL zu navigieren, in diesem Fall zur geschützt Route.

Definieren Sie den Login-API-Endpunkt

Im Inneren src/app Verzeichnis, erstellen Sie einen neuen Ordner und benennen Sie ihn API. Fügen Sie in diesem Ordner einen neuen hinzu login/route.js Datei und fügen Sie den folgenden Code ein.

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 });
}

Die Hauptaufgabe dieser API besteht darin, die in den POST-Anfragen übergebenen Anmeldeinformationen mithilfe von Scheindaten zu überprüfen.

Nach erfolgreicher Überprüfung wird ein verschlüsseltes JWT-Token generiert, das den authentifizierten Benutzerdetails zugeordnet ist. Schließlich wird eine erfolgreiche Antwort an den Client gesendet, einschließlich des Tokens in den Antwortcookies. andernfalls wird eine Fehlerstatusantwort zurückgegeben.

Implementieren Sie die Logik zur Token-Verifizierung

Der erste Schritt der Token-Authentifizierung ist die Generierung des Tokens nach einem erfolgreichen Anmeldevorgang. Der nächste Schritt besteht darin, die Logik für die Token-Verifizierung zu implementieren.

Im Wesentlichen verwenden Sie die jwtVerify Funktion, die von der bereitgestellt wird José Modul zur Überprüfung der JWT-Tokens, die mit nachfolgenden HTTP-Anfragen übergeben werden.

Im src Verzeichnis, erstellen Sie ein neues libs/auth.js Datei und fügen Sie den folgenden Code ein.

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;
}
}

Der geheime Schlüssel wird zum Signieren und Überprüfen der Token verwendet. Durch den Vergleich der entschlüsselten Token-Signatur mit der erwarteten Signatur kann der Server effektiv überprüfen, ob das bereitgestellte Token gültig ist, und letztendlich die Anforderungen der Benutzer autorisieren.

Erstellen .env Datei im Stammverzeichnis und fügen Sie wie folgt einen eindeutigen geheimen Schlüssel hinzu:

NEXT_PUBLIC_JWT_SECRET_KEY=your_secret_key

Erstellen Sie eine geschützte Route

Jetzt müssen Sie eine Route erstellen, auf die nur authentifizierte Benutzer Zugriff erhalten. Erstellen Sie dazu ein neues protected/page.js Datei in der src/app Verzeichnis. Fügen Sie in dieser Datei den folgenden Code hinzu.

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

Erstellen Sie einen Hook, um den Authentifizierungsstatus zu verwalten

Erstellen Sie einen neuen Ordner im src Verzeichnis und benennen Sie es Haken. Fügen Sie in diesem Ordner einen neuen hinzu useAuth/index.js Datei und fügen Sie den folgenden Code ein.

"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;
}

Dieser Hook verwaltet den Authentifizierungsstatus auf der Clientseite. Es ruft die Gültigkeit des in Cookies vorhandenen JWT-Tokens mithilfe von ab und überprüft es überprüfenJwtToken Funktion und setzt dann die authentifizierten Benutzerdetails auf die Autor Zustand.

Auf diese Weise können andere Komponenten auf die Informationen des authentifizierten Benutzers zugreifen und diese nutzen. Dies ist wichtig für Szenarien wie das Durchführen von UI-Aktualisierungen basierend auf dem Authentifizierungsstatus, das Durchführen nachfolgender API-Anfragen oder das Rendern unterschiedlicher Inhalte basierend auf Benutzerrollen.

In diesem Fall verwenden Sie den Hook, um verschiedene Inhalte auf dem zu rendern heim Route basierend auf dem Authentifizierungsstatus eines Benutzers.

Ein alternativer Ansatz, den Sie in Betracht ziehen könnten, ist die Handhabung Zustandsverwaltung mit Redux Toolkit oder die Anstellung eines State-Management-Tool wie Jotai. Dieser Ansatz garantiert, dass Komponenten globalen Zugriff auf den Authentifizierungsstatus oder jeden anderen definierten Status erhalten können.

Machen Sie weiter und öffnen Sie die app/page.js Datei, löschen Sie den Boilerplate-Next.js-Code und fügen Sie den folgenden Code hinzu.

"use client" ;

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

Public Home Page</h1>

Der obige Code verwendet die useAuth Hook zum Verwalten des Authentifizierungsstatus. Dabei wird bedingt eine öffentliche Homepage mit einem Link zur erstellt Anmeldung Page-Route, wenn der Benutzer nicht authentifiziert ist, und zeigt einen Absatz für einen authentifizierten Benutzer an.

Fügen Sie eine Middleware hinzu, um autorisierten Zugriff auf geschützte Routen zu erzwingen

Im src Verzeichnis, erstellen Sie ein neues middleware.js Datei und fügen Sie den folgenden Code hinzu.

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*"] };

Dieser Middleware-Code fungiert als Wächter. Es stellt sicher, dass Benutzer, die auf geschützte Seiten zugreifen möchten, authentifiziert und zum Zugriff auf die Routen berechtigt sind. Darüber hinaus werden nicht autorisierte Benutzer auf die Anmeldeseite umgeleitet.

Sicherung von Next.js-Anwendungen

Die Token-Authentifizierung ist ein wirksamer Sicherheitsmechanismus. Dies ist jedoch nicht die einzige verfügbare Strategie, um Ihre Anwendungen vor unbefugtem Zugriff zu schützen.

Um Anwendungen gegen die dynamische Cybersicherheitslandschaft zu wappnen, ist es wichtig, umfassende Sicherheitsmaßnahmen einzuführen Ansatz, der potenzielle Sicherheitslücken und Schwachstellen ganzheitlich anspricht, um eine umfassende Sicherheit zu gewährleisten Schutz.