Heim >Web-Frontend >js-Tutorial >Der richtige Weg zur Authentifizierung in Node.js [uide]

Der richtige Weg zur Authentifizierung in Node.js [uide]

Barbara Streisand
Barbara StreisandOriginal
2024-12-12 20:08:17657Durchsuche

The Right Way to do Authentication in Node.js [uide]

Authentifizierung ist einer der kritischsten, aber oft missverstandenen Aspekte der Backend-Entwicklung. Aufgrund seiner Komplexität greifen Entwickler häufig auf Lösungen von Drittanbietern wie Auth0 oder Supabase zurück. Obwohl dies hervorragende Tools sind, kann der Aufbau eines eigenen Authentifizierungssystems für mehr Flexibilität und Kontrolle sorgen.

In diesem Leitfaden erfahren Sie, wie Sie eine einfache Authentifizierungs-Middleware für Ihren Express.js-API-Dienst mit minimalen Abhängigkeiten implementieren. Am Ende haben Sie:

  • Voll funktionsfähige Authentifizierung mit Benutzername und Passwort.
  • Integration mit PostgreSQL zum Speichern von Benutzerkonten.
  • Eine JWT-basierte Authentifizierungs-Middleware.
  • Aktualisieren Sie Token mit automatischer Wiederverwendungserkennung für mehr Sicherheit.

Dieser Leitfaden konzentriert sich auf Einfachheit und vermeidet die Verwendung von Paketen wie „passport.js“, um die Komplexität zu reduzieren.


Einrichten der Benutzerkontentabelle

Erstellen Sie zunächst eine PostgreSQL-Tabelle zum Speichern von Benutzerkonten:

CREATE TABLE users (
    "id" SERIAL PRIMARY KEY,
    "username" VARCHAR(255) UNIQUE NOT NULL,
    "password" VARCHAR(255) NOT NULL,
    "email" VARCHAR(255) UNIQUE,
    "created_at" TIMESTAMP NOT NULL DEFAULT NOW()
);

JWT-Authentifizierungs-Middleware

Als nächstes erstellen Sie eine JWT-Authentifizierungs-Middleware, um API-Endpunkte zu schützen. In diesem Beispiel wird die symmetrische Verschlüsselung verwendet. Erwägen Sie für Microservices-Architekturen die Verwendung einer asymmetrischen Verschlüsselung mit einem öffentlichen/privaten Schlüsselpaar.

Middleware-Code (/src/middleware/jwt.ts):

import jwt from "jsonwebtoken";

const JWT_SECRET_KEY = process.env.JWT_SECRET_KEY as string; // Randomly generated. Min length: 64 characters

export const protectedRoute: RequestHandler = async (req, _, next) => {
  const authHeader = req.header("authorization");

  if (!authHeader) {
    return next(notAuthenticated());
  }

  const accessToken = authHeader.replace(new RegExp("\b[Bb]earer\s"), "");

  try {
    const { userId } = validateJWT(accessToken);
    const user = await userRepository.getUserById(parseInt(userId));

    if (user) {
      req.user = user;
      next();
    } else {
      next(invalidAccessToken());
    }
  } catch (err) {
    next(invalidAccessToken());
  }
};

const validateJWT = (token: string, verifyOptions?: jwt.VerifyOptions) => {
  const jwtVerifyOptions = Object.assign(
    { algorithms: "HS256" },
    verifyOptions,
    {
      issuer: "yourAPI.com",
      audience: "yourAPI.com:client",
    }
  );
  return jwt.verify(token, JWT_SECRET_KEY, jwtVerifyOptions) as T;
};

Verwenden Sie die Middleware, um Routen zu sichern:

import { protectedRoute } from "@/middleware/jwt";

router.get("/user", protectedRoute, async (req, res, next) => {
  const user = req.user!;
  res.json({ user });
});

Authentifizierungscontroller erstellen

Jetzt implementieren Sie Controller für die Anmeldung und Anmeldung:

Anmeldecontroller:

import argon from "argon2";

const signup = async (props) => {
  const { username, password, email } = props;

  await userRepo.getUser(username).then((res) => {
    if (res !== null) throw usernameNotAvailable();
  });

  const hashedPass = await argon.hash(password, {
    timeCost: 2,
    parallelism: 1,
    memoryCost: 19456,
  });

  const newUser = await createUser({
    username,
    hashedPass,
    email,
  });

  const refreshToken = await generateRefreshToken(newUser.userId);
  const accessToken = generateAccessToken(newUser.userId);

  const { password: _, ...userRes } = newUser;
  return { user: userRes, accessToken, refreshToken };
};

Login-Controller:

const login = async (props) => {
  const { username, password } = props;

  const user = await getUser(username).then((res) => {
    if (res === null) throw invalidLoginCredentials();
    return res;
  });

  const isOk = await argon.verify(user.password, password);

  if (isOk) {
    const refreshToken = await generateRefreshToken(user.userId);
    const accessToken = generateAccessToken(user.userId);

    const { password: _, ...userRes } = user;
    return { user: userRes, accessToken, refreshToken };
  }

  throw invalidLoginCredentials();
};

Speichern von Aktualisierungstoken

Aktualisierungstoken bieten eine langfristige Authentifizierung. Erstellen wir eine Datenbanktabelle, um sie zu speichern:

CREATE TABLE refresh_tokens (
    "id" SERIAL PRIMARY KEY,
    "token" UUID NOT NULL DEFAULT gen_random_uuid(),
    "token_family" UUID NOT NULL DEFAULT gen_random_uuid(),
    "user_id" INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    "active" BOOLEAN DEFAULT true,
    "expires_at" TIMESTAMP NOT NULL,
    "created_at" TIMESTAMP NOT NULL DEFAULT NOW()
);

Token-Generatoren:

import jwt from "jsonwebtoken";

const JWT_SECRET_KEY = process.env.JWT_SECRET_KEY as string; // Randomly generated. Min length: 64 characters

const generateAccessToken = (userId: number) => {
  const jwtSignOptions = Object.assign(
    { algorithm: "HS256" },
    {},
    {
      issuer: "yourAPI.com",
      audience: "yourAPI.com:client",
    }
  );
  return jwt.sign({ userId: userId.toString() }, JWT_SECRET_KEY, jwtSignOptions);
};

const generateRefreshToken = async (userId: number, tokenFamily?: string) => {
  const expAt = new Date(new Date().getTime() + 31 * 24 * 60 * 60 * 1000); // Expire in 31 days
  const refreshTokenExp = expAt.toISOString();

  const token = await createTokenQuery({
    userId,
    tokenFamily,
    expiresAt: refreshTokenExp,
  });

  return token;
};

Token-Logik aktualisieren:

Logik implementieren, um Aktualisierungstokens sicher zu verarbeiten:

const refreshToken = async ({ token }: RefreshTokenSchema) => {
  const tokenData = await getRefreshToken(token);

  if (!tokenData) throw forbiddenError();

  const { userId, tokenFamily, active } = tokenData;

  if (active) {
    // Token is valid and hasn't been used yet
    const newRefreshToken = await generateRefreshToken(userId, tokenFamily);
    const accessToken = generateAccessToken(userId);

    return { accessToken, refreshToken: newRefreshToken };
  } else {
    // Previously refreshed token used, invalidate all tokens in family
    await invalidateRefreshTokenFamily(tokenFamily);

    throw forbiddenError();
  }
};

Erfahren Sie mehr über Aktualisierungstokens und die automatische Wiederverwendungserkennung in diesem Auth0-Artikel.


Abschluss

Indem Sie dieser Anleitung folgen, haben Sie ein einfaches, sicheres Authentifizierungssystem für Ihre Node.js-API mit minimalen Abhängigkeiten erstellt. Dieser Ansatz stellt sicher, dass Sie die volle Kontrolle haben und moderne Best Practices für die Sicherheit einhalten.

Wenn Sie Zeit und Mühe sparen möchten, schauen Sie sich Vratix an. Unsere Open-Source-CLI kann in Sekundenschnelle ein voll funktionsfähiges Node.js-Projekt mit Authentifizierung einrichten. Entdecken Sie unser vollständig implementiertes Authentifizierungsmodul auf GitHub.


Hat Ihnen dieser Leitfaden geholfen? Lass es uns in den Kommentaren wissen oder vernetze dich mit uns auf X!

Das obige ist der detaillierte Inhalt vonDer richtige Weg zur Authentifizierung in Node.js [uide]. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn