Uzyskaj większą kontrolę nad logiką uwierzytelniania aplikacji Next.js dzięki niestandardowej implementacji uwierzytelniania opartej na JWT.
Uwierzytelnianie tokenowe to popularna strategia stosowana w ochronie aplikacji internetowych i mobilnych przed nieautoryzowanym dostępem. W Next.js możesz korzystać z funkcji uwierzytelniania udostępnianych przez Next-auth.
Alternatywnie możesz zdecydować się na opracowanie niestandardowego systemu uwierzytelniania opartego na tokenach przy użyciu tokenów internetowych JSON (JWT). Robiąc to, masz pewność, że masz większą kontrolę nad logiką uwierzytelniania; zasadniczo, dostosowywanie systemu tak, aby dokładnie odpowiadał wymaganiom Twojego projektu.
Skonfiguruj projekt Next.js
Aby rozpocząć, zainstaluj Next.js, uruchamiając poniższe polecenie na swoim terminalu.
npx create-next-app@latest next-auth-jwt --experimental-app
Ten przewodnik będzie używany Next.js 13, który zawiera katalog aplikacji.
Następnie zainstaluj te zależności w swoim projekcie, używając npm, menedżer pakietów węzłów.
npm install jose universal-cookie
Jose to moduł JavaScript udostępniający zestaw narzędzi do pracy z tokenami sieciowymi JSON, podczas gdy plik uniwersalne-cookie zależność zapewnia prosty sposób pracy z plikami cookie przeglądarki zarówno po stronie klienta, jak i po stronie serwera.
Kod tego projektu znajdziesz w this Repozytorium GitHuba.
Utwórz interfejs użytkownika formularza logowania
Otworzyć źródło/aplikacja katalog, utwórz nowy folder i nadaj mu nazwę Zaloguj sie. Wewnątrz tego folderu dodaj nowy strona.js plik i dołącz poniższy kod.
"use client";
import { useRouter } from"next/navigation";
exportdefaultfunctionLoginPage() {
return (
Powyższy kod tworzy komponent funkcjonalny strony logowania, który wyświetli w przeglądarce prosty formularz logowania, umożliwiający użytkownikom wprowadzenie nazwy użytkownika i hasła.
The użyj klienta instrukcja w kodzie gwarantuje, że w pliku zostanie zadeklarowana granica pomiędzy kodem przeznaczonym tylko dla serwera i kodem przeznaczonym wyłącznie dla klienta aplikacja informator.
W tym przypadku służy do zadeklarowania, że kod na stronie logowania, w szczególności uchwytPrześlijfunkcja jest wykonywana tylko na kliencie; w przeciwnym razie Next.js zgłosi błąd.
Teraz zdefiniujmy kod dla pliku uchwytPrześlij funkcjonować. Wewnątrz komponentu funkcjonalnego dodaj następujący kod.
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");
}
};
Aby zarządzać logiką uwierzytelniania logowania, funkcja ta przechwytuje dane uwierzytelniające użytkownika z formularza logowania. Następnie wysyła żądanie POST do punktu końcowego API, przekazując dane użytkownika w celu weryfikacji.
Jeśli dane uwierzytelniające są prawidłowe, co oznacza, że proces logowania przebiegł pomyślnie, interfejs API zwraca w odpowiedzi informację o powodzeniu. Funkcja obsługi użyje następnie routera Next.js do nawigowania użytkownika do określonego adresu URL, w tym przypadku do chroniony trasa.
Zdefiniuj punkt końcowy interfejsu API logowania
W środku źródło/aplikacja katalogu, utwórz nowy folder i nadaj mu nazwę API. Wewnątrz tego folderu dodaj nowy login/route.js plik i dołącz poniższy kod.
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 });
}
Podstawowym zadaniem tego interfejsu API jest weryfikacja danych logowania przekazywanych w żądaniach POST przy użyciu fałszywych danych.
Po pomyślnej weryfikacji generuje zaszyfrowany token JWT powiązany z danymi uwierzytelnionego użytkownika. Na koniec wysyła do klienta pomyślną odpowiedź, zawierającą token w plikach cookie odpowiedzi; w przeciwnym razie zwraca odpowiedź dotyczącą stanu błędu.
Zaimplementuj logikę weryfikacji tokenu
Początkowym krokiem w uwierzytelnianiu tokenem jest wygenerowanie tokena po pomyślnym procesie logowania. Kolejnym krokiem jest wdrożenie logiki weryfikacji tokena.
Zasadniczo będziesz używać jwtZweryfikuj funkcja oferowana przez Jose moduł do weryfikacji tokenów JWT przekazywanych z kolejnymi żądaniami HTTP.
w źródło katalog, utwórz nowy libs/auth.js plik i dołącz poniższy kod.
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;
}
}
Tajny klucz służy do podpisywania i weryfikacji tokenów. Porównując zdekodowany podpis tokena z oczekiwanym podpisem, serwer może skutecznie zweryfikować, czy dostarczony token jest ważny i ostatecznie autoryzować żądania użytkowników.
Tworzyć .środka plik w katalogu głównym i dodaj unikalny tajny klucz w następujący sposób:
NEXT_PUBLIC_JWT_SECRET_KEY=your_secret_key
Utwórz chronioną trasę
Teraz musisz utworzyć trasę, do której dostęp będą mogli uzyskać tylko uwierzytelnieni użytkownicy. Aby to zrobić, utwórz nowy chronione/strona.js plik w źródło/aplikacja informator. Wewnątrz tego pliku dodaj następujący kod.
exportdefaultfunctionProtectedPage() {
return<h1>Very protected pageh1>;
}
Utwórz hak do zarządzania stanem uwierzytelniania
Utwórz nowy folder w źródło katalog i nadaj mu nazwę haczyki. Wewnątrz tego folderu dodaj nowy useAuth/index.js plik i dołącz poniższy kod.
"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;
}
Ten hak zarządza stanem uwierzytelniania po stronie klienta. Pobiera i weryfikuje ważność tokena JWT obecnego w plikach cookie za pomocą zweryfikujJwtToken funkcję, a następnie ustawia dane uwierzytelnionego użytkownika na autoryzacja państwo.
W ten sposób umożliwia innym komponentom dostęp do informacji uwierzytelnionego użytkownika i ich wykorzystanie. Jest to niezbędne w scenariuszach takich jak aktualizowanie interfejsu użytkownika na podstawie stanu uwierzytelnienia, wysyłanie kolejnych żądań do interfejsu API lub renderowanie różnych treści w oparciu o role użytkowników.
W tym przypadku użyjesz haka do renderowania różnych treści na dom trasa na podstawie stanu uwierzytelnienia użytkownika.
Alternatywnym podejściem, które możesz rozważyć, jest obsługa zarządzanie stanem za pomocą Redux Toolkit lub zatrudnianie A narzędzie do zarządzania stanem, takie jak Jotai. Takie podejście gwarantuje, że komponenty mogą uzyskać globalny dostęp do stanu uwierzytelniania lub dowolnego innego zdefiniowanego stanu.
Śmiało, otwórz app/page.js plik, usuń szablonowy kod Next.js i dodaj następujący kod.
"use client" ;
import { useAuth } from"@/hooks/useAuth";
import Link from"next/link";
exportdefaultfunctionHome() {
const auth = useAuth();
return<>Public Home Page</h1>
Powyższy kod wykorzystuje użyj autoryzacji hak do zarządzania stanem uwierzytelniania. W ten sposób warunkowo renderuje publiczną stronę główną z łączem do Zaloguj sie page Route, gdy użytkownik nie jest uwierzytelniony, i wyświetla akapit dla uwierzytelnionego użytkownika.
Dodaj oprogramowanie pośredniczące, aby wymusić autoryzowany dostęp do chronionych tras
w źródło katalog, utwórz nowy oprogramowanie pośrednie.js plik i dodaj poniższy kod.
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*"] };
Ten kod oprogramowania pośredniczącego działa jako strażnik. Sprawdza, czy użytkownicy chcący uzyskać dostęp do chronionych stron są uwierzytelniani i upoważnieni do dostępu do tras, a także przekierowują nieautoryzowanych użytkowników na stronę logowania.
Zabezpieczanie aplikacji Next.js
Uwierzytelnianie tokenem jest skutecznym mechanizmem bezpieczeństwa. Nie jest to jednak jedyna dostępna strategia ochrony aplikacji przed nieautoryzowanym dostępem.
Aby wzmocnić aplikacje pod kątem dynamicznego krajobrazu cyberbezpieczeństwa, ważne jest przyjęcie kompleksowych zabezpieczeń podejście, które całościowo eliminuje potencjalne luki i luki w zabezpieczeniach, aby zagwarantować dokładność ochrona.