Aveți mai mult control asupra logicii de autentificare a aplicației dvs. Next.js prin implementarea personalizată a autentificării bazate pe JWT.
Autentificarea cu simboluri este o strategie populară folosită pentru a proteja aplicațiile web și mobile împotriva accesului neautorizat. În Next.js, puteți utiliza funcțiile de autentificare oferite de Next-auth.
Alternativ, puteți opta pentru a dezvolta un sistem de autentificare personalizat bazat pe token-uri folosind JSON Web Tokens (JWT-uri). Procedând astfel, vă asigurați că aveți mai mult control asupra logicii de autentificare; în esență, personalizarea sistemului pentru a se potrivi exact cerințelor proiectului dumneavoastră.
Configurați un proiect Next.js
Pentru a începe, instalați Next.js rulând comanda de mai jos pe terminalul dvs.
npx create-next-app@latest next-auth-jwt --experimental-app
Acest ghid va folosi Next.js 13 care include directorul aplicației.
Apoi, instalați aceste dependențe în proiectul dvs. folosind npm, Managerul de pachete Node.
npm install jose universal-cookie
Jose este un modul JavaScript care oferă un set de utilități pentru lucrul cu jetoane web JSON în timp ce universal-cookie dependența oferă o modalitate simplă de a lucra cu module cookie de browser atât în mediul client, cât și pe cel server.
Puteți găsi codul acestui proiect în aceasta Depozitul GitHub.
Creați interfața de utilizator pentru formularul de conectare
Deschide src/app director, creați un folder nou și denumiți-l log in. În interiorul acestui folder, adăugați unul nou page.js fișier și includeți codul de mai jos.
"use client";
import { useRouter } from"next/navigation";
exportdefaultfunctionLoginPage() {
return (
Codul de mai sus creează o componentă funcțională a paginii de conectare care va afișa un simplu formular de conectare în browser pentru a permite utilizatorilor să introducă un nume de utilizator și o parolă.
The utilizați clientul declarația din cod asigură că este declarată o limită între codul doar pentru server și codul numai pentru client în aplicația director.
În acest caz, este folosit pentru a declara că codul din pagina de autentificare, în special, codul handleSubmitfuncția este executată numai pe client; în caz contrar, Next.js va arunca o eroare.
Acum, să definim codul pentru handleSubmit funcţie. În interiorul componentei funcționale, adăugați următorul cod.
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");
}
};
Pentru a gestiona logica de autentificare, această funcție captează acreditările utilizatorului din formularul de conectare. Apoi trimite o solicitare POST către un punct final API care transmite detaliile utilizatorului pentru verificare.
Dacă acreditările sunt valide, indicând că procesul de conectare a avut succes — API-ul returnează o stare de succes în răspuns. Funcția de gestionare va folosi apoi routerul Next.js pentru a naviga pe utilizator la o adresă URL specificată, în acest caz, protejat traseu.
Definiți punctul final al API-ului de conectare
În interiorul src/app director, creați un folder nou și denumiți-l api. În interiorul acestui folder, adăugați unul nou login/route.js fișier și includeți codul de mai jos.
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 });
}
Sarcina principală pentru acest API este să verifice acreditările de conectare transmise în solicitările POST folosind date simulate.
După verificarea cu succes, generează un token JWT criptat asociat cu detaliile utilizatorului autentificat. În cele din urmă, trimite un răspuns de succes clientului, inclusiv token-ul în cookie-urile de răspuns; în caz contrar, returnează un răspuns de stare de eșec.
Implementați logica de verificare a simbolurilor
Pasul inițial în autentificarea jetonului este generarea jetonului după un proces de conectare cu succes. Următorul pas este implementarea logicii pentru verificarea simbolului.
În esență, veți folosi jwtVerify funcția oferită de Jose modul pentru a verifica jetoanele JWT transmise cu solicitările HTTP ulterioare.
În src director, creați un nou libs/auth.js fișier și includeți codul de mai jos.
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;
}
}
Cheia secretă este utilizată în semnarea și verificarea jetoanelor. Comparând semnătura simbolului decodificat cu semnătura așteptată, serverul poate verifica în mod eficient dacă simbolul furnizat este valid și, în cele din urmă, poate autoriza solicitările utilizatorilor.
Crea .env fișier în directorul rădăcină și adăugați o cheie secretă unică după cum urmează:
NEXT_PUBLIC_JWT_SECRET_KEY=your_secret_key
Creați o rută protejată
Acum, trebuie să creați o rută la care numai utilizatorii autentificați pot avea acces. Pentru a face acest lucru, creați un nou protected/page.js dosar în src/app director. În interiorul acestui fișier, adăugați următorul cod.
exportdefaultfunctionProtectedPage() {
return<h1>Very protected pageh1>;
}
Creați un cârlig pentru a gestiona starea de autentificare
Creați un folder nou în src director și denumește-l cârlige. În interiorul acestui folder adăugați un nou useAuth/index.js fișier și includeți codul de mai jos.
"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;
}
Acest cârlig gestionează starea de autentificare pe partea clientului. Preia și verifică validitatea simbolului JWT prezent în cookie-uri folosind verifyJwtToken funcția și apoi setează detaliile utilizatorului autentificat la auth stat.
Procedând astfel, permite altor componente să acceseze și să utilizeze informațiile utilizatorului autentificat. Acest lucru este esențial pentru scenarii precum realizarea de actualizări ale interfeței de utilizare pe baza stării de autentificare, efectuarea de solicitări API ulterioare sau redarea de conținut diferit în funcție de rolurile utilizatorului.
În acest caz, veți folosi cârligul pentru a reda conținut diferit pe Acasă traseu bazat pe starea de autentificare a unui utilizator.
O abordare alternativă pe care o puteți lua în considerare este manipularea managementul de stat folosind Redux Toolkit sau angajarea unui instrument de management al statului precum Jotai. Această abordare garantează că componentele pot obține acces global la starea de autentificare sau la orice altă stare definită.
Continuați și deschideți app/page.js fișier, ștergeți codul boilerplate Next.js și adăugați următorul cod.
"use client" ;
import { useAuth } from"@/hooks/useAuth";
import Link from"next/link";
exportdefaultfunctionHome() {
const auth = useAuth();
return<>Public Home Page</h1>
Codul de mai sus utilizează useAuth cârlig pentru a gestiona starea de autentificare. Făcând acest lucru, redă condiționat o pagină de pornire publică cu un link către log in ruta paginii când utilizatorul nu este autentificat și afișează un paragraf pentru un utilizator autentificat.
Adăugați un middleware pentru a impune accesul autorizat la rutele protejate
În src director, creați un nou middleware.js fișier și adăugați codul de mai jos.
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*"] };
Acest cod middleware acționează ca un gardian. Acesta verifică pentru a se asigura că atunci când utilizatorii doresc să acceseze paginile protejate, aceștia sunt autentificați și autorizați să acceseze rutele, în plus față de redirecționarea utilizatorilor neautorizați către pagina de autentificare.
Securizarea aplicațiilor Next.js
Autentificarea prin token este un mecanism de securitate eficient. Cu toate acestea, nu este singura strategie disponibilă pentru a vă proteja aplicațiile împotriva accesului neautorizat.
Pentru a consolida aplicațiile față de peisajul dinamic al securității cibernetice, este important să adoptați o securitate cuprinzătoare abordare care abordează în mod holistic lacunele și vulnerabilitățile potențiale de securitate pentru a garanta minuțiozitatea protecţie.