Pengesahan ialah salah satu aspek pembangunan bahagian belakang yang paling kritikal namun sering disalahfahamkan. Disebabkan kerumitannya, pembangun kerap beralih kepada penyelesaian pihak ketiga seperti Auth0 atau Supabase. Walaupun ini adalah alat yang sangat baik, membina sistem pengesahan anda sendiri boleh memberikan fleksibiliti dan kawalan yang lebih besar.
Dalam panduan ini, anda akan belajar cara melaksanakan perisian tengah pengesahan mudah untuk perkhidmatan API Express.js anda dengan kebergantungan yang minimum. Pada akhirnya, anda akan mempunyai:
Panduan ini memfokuskan pada kesederhanaan dan mengelakkan penggunaan pakej seperti passport.js untuk mengurangkan kerumitan.
Pertama, buat jadual PostgreSQL untuk menyimpan akaun pengguna:
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() );
Seterusnya, cipta perisian tengah pengesahan JWT untuk melindungi titik akhir API. Contoh ini menggunakan penyulitan simetri. Untuk seni bina perkhidmatan mikro, pertimbangkan untuk menggunakan penyulitan asimetri dengan pasangan kunci awam/peribadi.
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; };
Gunakan perisian tengah untuk menjamin laluan:
import { protectedRoute } from "@/middleware/jwt"; router.get("/user", protectedRoute, async (req, res, next) => { const user = req.user!; res.json({ user }); });
Sekarang, laksanakan pengawal untuk pendaftaran dan log masuk:
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 }; };
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(); };
Token muat semula memberikan pengesahan jangka panjang. Mari buat jadual pangkalan data untuk menyimpannya:
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() );
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; };
Laksanakan logik untuk mengendalikan token yang menyegarkan dengan selamat:
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(); } };
Ketahui lebih lanjut tentang token muat semula dan Pengesanan Penggunaan Semula Automatik dalam artikel Auth0 ini.
Dengan mengikuti panduan ini, anda telah membina sistem pengesahan yang mudah dan selamat untuk API Node.js anda dengan kebergantungan yang minimum. Pendekatan ini memastikan anda mempunyai kawalan penuh dan mematuhi amalan terbaik moden untuk keselamatan.
Jika anda ingin menjimatkan masa dan usaha, lihat Vratix. CLI sumber terbuka kami boleh menyediakan projek Node.js yang berfungsi sepenuhnya dengan pengesahan dalam beberapa saat. Terokai modul pengesahan kami yang dilaksanakan sepenuhnya di GitHub.
Adakah panduan ini membantu anda? Beritahu kami dalam ulasan atau berhubung dengan kami di X!
Atas ialah kandungan terperinci Cara yang Betul untuk melakukan Pengesahan dalam Node.js [uide]. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!