השתמש בארכיטקטורה המובנית של Nest כדי לבנות ממשקי API של REST מאובטחים ויעילים.

Express.js היא טכנולוגיה מצוינת לבניית ממשקי API של REST מאובטחים ואיתן, עם זאת, היא אינה מספקת מבנה מוגדר מראש. האופי המינימליסטי שלו מאפשר לך לטפל בהיבטים חיוניים כמו ניתוב, ארגון קוד ואמצעי אבטחה באופן ידני או על ידי מינוף תוכנות ביניים וספריות זמינות.

לעומת זאת, Nest.js, שנבנה על גבי Express.js ו-Node.js, מציג הפשטה ברמה גבוהה יותר שמציע מבנה ברור, גישת ארגון קוד חזקה ויישום פשוט פרטים. בעיקרו של דבר, Nest.js מספקת ארכיטקטורה מובנית יותר לבניית ממשקי API ושירותים אחוריים יעילים ומאובטחים.

הגדרת פרויקט Nest.js

כדי להתחיל, תחילה עליך להתקין את שורת הפקודה (CLI) של Nest.js באופן גלובלי על ידי הפעלת הפקודה למטה:

npm i -g @nestjs/cli

לאחר השלמת ההתקנה, קדימה, צור פרויקט חדש על ידי הפעלת:

קן חדש nest-jwt-api

לאחר מכן, Nest.js CLI ינחה אותך לבחור מנהל חבילות להתקנת התלות. עבור הדרכה זו, אנו נשתמש npm, מנהל חבילות הצומת. בחר npm והמתן בזמן שה-CLI יוצר פרויקט Nest.js בסיסי ומתקין את כל קבצי התצורה הנדרשים והתלות הראשונית הנדרשים להפעלת האפליקציה.

instagram viewer

לאחר הגדרת הפרויקט, נווט אל ספריית הפרויקט והפעל את שרת הפיתוח.

cd nest-jwt-api
הפעלת npm run

לבסוף, הפעל את הפקודה למטה כדי להתקין את החבילות שבהן נשתמש עבור פרויקט זה.

npm להתקין mongodb mongoose @nestjs/mongoose @types/bcrypt bcrypt jsonwebtoken @nestjs/jwt

אתה יכול למצוא את הקוד של הפרויקט הזה בזה מאגר GitHub.

הגדר את חיבור מסד הנתונים של MongoDB

הגדר מסד נתונים MongoDB באופן מקומי אוֹ להגדיר אשכול MongoDB בענן. לאחר הגדרת מסד הנתונים, העתק את מחרוזת ה-URI של חיבור מסד הנתונים, צור א .env קובץ בספריית השורש של תיקיית הפרויקט שלנו, והדבק במחרוזת החיבור:

MONGO_URI="מחרוזת חיבור"

לאחר מכן, עדכן את app.module.ts בתוך ה src קובץ ספרייה להגדרת Mongoose באופן הבא:

יְבוּא { מודול } מ'@nestjs/common';
יְבוּא { ConfigModule } מ'@nestjs/config';
יְבוּא { MongooseModule } מ'@nestjs/mongoose';
יְבוּא { AppController } מ'./app.controller';
יְבוּא { AppService } מ'./app.service';
יְבוּא { UserAuthModule } מ'./user-auth/user-auth.module';

@מודול({
יבוא: [
ConfigModule.forRoot({
envFilePath: '.env',
isGlobal: נָכוֹן,
}),
MongooseModule.forRoot (process.env. MONGO_URI),
UserAuthModule,
],
בקרים: [AppController],
ספקים: [AppService],
})

יְצוּאמעמד AppModule {}

הקוד שסופק מגדיר שלושה מודולים חיוניים עבור היישום Nest.js: ConfigModule עבור תצורת סביבה, MongooseModule ליצירת חיבור MongoDB, ו UserAuthModule לאימות משתמש. שים לב שבשלב זה עלולה להתרחש שגיאה מאז ה UserAuthModule עדיין לא מוגדר, אבל ניצור אותו בסעיף הבא.

יצירת מודול אימות המשתמש

כדי לשמור על קוד נקי ומאורגן היטב, צור מודול אימות משתמש על ידי הפעלת הפקודה הבאה.

nest g module user-auth

כלי Nest.js CLI מייצר אוטומטית את קבצי המודול הנדרשים. בנוסף, הוא יעדכן את app.module.ts קובץ, המשלב את השינויים הדרושים הקשורים למודול אימות המשתמש.

אתה יכול לבחור ליצור את קבצי תצורת הפרויקט הראשיים באופן ידני, עם זאת, כלי ה-CLI מפשט תהליך זה על ידי יצירה אוטומטית של הפריטים הנדרשים, בנוסף לעדכון השינויים בהתאם ב ה app.module.ts קוֹבֶץ.

צור סכימת משתמש

בתוך החדש שנוצר אישור משתמש תיקייה ב- src ספרייה, צור חדש schemas/user-auth.schema.ts קובץ, והוסיפו את הקוד הבא כדי ליצור סכימת Mongoose עבור מִשׁתַמֵשׁ דֶגֶם

יְבוּא { Prop, Schema, SchemaFactory } מ'@nestjs/mongoose';
יְבוּא { מסמך } מ'נְמִיָה';

@סכֵימָה({ חותמות זמן: נָכוֹן })
יְצוּאמעמד משתמש {
@לִתְמוֹך()
שם משתמש: חוּט;
@לִתְמוֹך()
סיסמה: חוּט;
}

יְצוּאסוּג UserDocument = משתמש ומסמך;
יְצוּאconst UserSchema = SchemaFactory.createForClass (משתמש);

יצירת שירות אימות המשתמש

כעת, בואו ניצור את שירות אימות המשתמש שינהל את לוגיקת האימות עבור REST API על ידי הפעלת הפקודה למטה:

nest g service user-aut

פקודה זו תיצור א user-auth.service.ts קובץ בתוך ספריית אישור משתמש. פתח את הקובץ הזה ועדכן אותו בקוד הבא.

  1. ראשית, בצע את הייבוא ​​הבא.
    יְבוּא { Injectable, NotFoundException, Logger, UnauthorizedException } מ'@nestjs/common';
    יְבוּא { InjectModel } מ'@nestjs/mongoose';
    יְבוּא { דגם } מ'נְמִיָה';
    יְבוּא { משתמש } מ'./schemas/user-auth.schema';
    יְבוּא * כפי ש bcrypt מ'bcrypt';
    יְבוּא { JwtService } מ'@nestjs/jwt';
  2. לאחר מכן, צור א UserAuthService מחלקה המכילה את הפונקציונליות לרישום משתמש, כניסה ואחזור של כל נתיבי נתוני המשתמש.
@ניתן להזרקה()
יְצוּאמעמד UserAuthService {
פְּרָטִי לוגר לקריאה בלבד = חָדָשׁ לוגר (UserAuthService.name);
בַּנַאִי(@InjectModel(שם משתמש) פְּרָטִי userModel: דגם, פְּרָטִי jwtService: JwtService) {}

אסינכרון registerUser (שם משתמש: חוּט, סיסמה: חוּט): הַבטָחָהחוּט }> {
לְנַסוֹת {
const hash = לְהַמתִין bcrypt.hash (סיסמה, 10);
לְהַמתִיןזֶה.userModel.create({ שם משתמש, סיסמה: hash });
לַחֲזוֹר { הודעה: 'משתמש נרשם בהצלחה' };
} לתפוס (שגיאה) {
לזרוקחָדָשׁשְׁגִיאָה('אירעה שגיאה במהלך רישום המשתמש');
}
 }

אסינכרון loginUser (שם משתמש: חוּט, סיסמה: חוּט): הַבטָחָה<חוּט> {
לְנַסוֹת {
const משתמש = לְהַמתִיןזֶה.userModel.findOne({ שם משתמש });
אם (!משתמש) {
לזרוקחָדָשׁ NotFoundException('המשתמש לא נמצא');
}
const passwordMatch = לְהַמתִין bcrypt.compare (סיסמה, user.password);
אם (!passwordMatch) {
לזרוקחָדָשׁ UnauthorizedException('נתוני כניסה שגויים');
}
const מטען = { userId: user._id };
const אסימון = זֶה.jwtService.sign (מטען);
לַחֲזוֹר אֲסִימוֹן;
} לתפוס (שגיאה) {
לְנַחֵם.log (שגיאה);
לזרוקחָדָשׁ UnauthorizedException('אירעה שגיאה בעת הכניסה');
}
}

אסינכרון getUsers(): הַבטָחָה {
לְנַסוֹת {
const משתמשים = לְהַמתִיןזֶה.userModel.find({});
לַחֲזוֹר משתמשים;
} לתפוס (שגיאה) {
זֶה.logger.error(`אירעה שגיאה בעת אחזור משתמשים: ${error.message}`);
לזרוקחָדָשׁשְׁגִיאָה('אירעה שגיאה בעת אחזור משתמשים');
}
}
}

ה UserAuthService class מיישמת את ההיגיון של רישום משתמש, כניסה ושליפה של נתוני משתמש. הוא משתמש ב- UserModel לקיים אינטראקציה עם מסד הנתונים ולבצע את הפעולות הנדרשות כולל גיבוב הסיסמה במהלך רישום, אימות אישורי כניסה, ולבסוף, יצירת אסימוני JWT לאחר הצלחה אימות.

יישום משמר האימות

כדי להבטיח את האבטחה של משאבים רגישים, חיוני להגביל את הגישה למשתמשים מורשים בלבד. הדבר מושג על ידי אכיפת אמצעי אבטחה המחייב נוכחות של JWT חוקי בבקשות API עוקבות המבוצעות לנקודות קצה מוגנות, במקרה זה, משתמשים מַסלוּל. בתוך ה אישור משתמש ספרייה, צור חדש auth.guard.ts קובץ והוסף את הקוד למטה.

יְבוּא { CanActivate, ExecutionContext, Injectable, UnauthorizedException } מ'@nestjs/common';
יְבוּא { JwtService } מ'@nestjs/jwt';
יְבוּא { בקשה } מ'אֶקְסְפּרֶס';
יְבוּא { מפתח סודי } מ'./config';

@ניתן להזרקה()
יְצוּאמעמד AuthGuard מיישם CanActivate {
בַּנַאִי(פְּרָטִי jwtService: JwtService) {}

אסינכרון canActivate (הקשר: ExecutionContext): הַבטָחָה<בוליאני> {
const request = context.switchToHttp().getRequest();
const אסימון = זֶה.extractTokenFromHeader (בקשה);
אם (!token) {
לזרוקחָדָשׁ UnauthorizedException();
}
לְנַסוֹת {
const מטען = לְהַמתִיןזֶה.jwtService.verifyAsync (אסימון, {
סוד: secretKey.secret,
});
בַּקָשָׁה['מִשׁתַמֵשׁ'] = מטען;
} לתפוס {
לזרוקחָדָשׁ UnauthorizedException();
}
לַחֲזוֹרנָכוֹן;
}
פְּרָטִי extractTokenFromHeader (בקשה: בקשה): חוּט | לא מוגדר {
const [סוּג, token] = request.headers.authorization?.split(' ')?? [];
לַחֲזוֹרסוּג'נוֹשֵׂא'? אֲסִימוֹן: לא מוגדר;
}
}

הקוד מיישם את א שומר, כפי שצוין בתיעוד הרשמי, כדי להגן על מסלולים ולהבטיח שרק משתמשים מאומתים עם אסימון JWT חוקי יכולים לגשת אליהם.

הוא מחלץ את אסימון JWT מכותרת הבקשה, מאמת את האותנטיות שלו באמצעות ה JwtService, ומקצה את המטען המפוענח ל- בקשה['משתמש'] נכס להמשך עיבוד. אם האסימון חסר או לא חוקי, הוא זורק UnauthorizedException כדי למנוע גישה למסלול המוגן.

עכשיו, צור config.ts קובץ באותה ספרייה, והוסף את הקוד למטה.

יְצוּאconst סוד מפתח = {
סוֹד: 'ערך סודי'.,
};

מפתח סודי זה משמש לחתימה ואימות האותנטיות של JWTs. חיוני לאחסן את ערך המפתח בצורה מאובטחת כדי למנוע גישה בלתי מורשית ולהגן על שלמות ה-JWTs.

הגדר את בקר ה-API

צור בקר המטפל בנקודות הקצה של ה-API לאימות משתמש.

אישור משתמש של nest g controller

לאחר מכן, העתק את הקוד שסופק כאן קובץ מאגר GitHub, והוסיפו אותו ל- user-auth.controller.ts קובץ - הוא מגדיר את נקודות הקצה לרישום משתמש, התחברות ואחזור נתוני משתמש. ה UseGuards (AuthGuard) דקורטור כלול כדי לאכוף אימות עבור getUsers נקודת קצה, המבטיחה שרק משתמשים מאומתים מקבלים גישה.

עדכן את קובץ user-auth.module.ts

כדי לשקף את השינויים שבוצעו בפרויקט, עדכן את ה user-auth.module.ts קובץ כדי להגדיר את המודולים, השירותים והבקרים הדרושים לאימות משתמש.

יְבוּא { מודול, NestModule, MiddlewareConsumer } מ'@nestjs/common';
יְבוּא { JwtModule } מ'@nestjs/jwt';
יְבוּא { UserAuthController } מ'./user-auth.controller';
יְבוּא { UserAuthService } מ'./user-auth.service';
יְבוּא { MongooseModule } מ'@nestjs/mongoose';
יְבוּא { UserSchema } מ'./schemas/user-auth.schema';
יְבוּא { מפתח סודי } מ'./config';

@מודול({
יבוא: [
MongooseModule.forFeature([{ שם: 'מִשׁתַמֵשׁ', סכימה: UserSchema }]),
JwtModule.register({
סוד: secretKey.secret,
signOptions: { expiresIn: 'שעה אחת' },
}),
],
בקרים: [UserAuthController],
ספקים: [UserAuthService],
})

יְצוּאמעמד UserAuthModule מיישם NestModule {
configure (צרכן: MiddlewareConsumer) {
}
}

לבסוף, הפוך את שרת הפיתוח ובדוק את נקודות הקצה של ה-API באמצעות Postman.

הפעלת npm run

בניית ממשקי API של Nest.js REST מאובטחים

בניית ממשקי API מאובטחים של Nest.js REST דורשת גישה מקיפה החורגת רק מהסתמכות על JWTs לצורך אימות והרשאה. אמנם JWTs חשובים, אך חשוב באותה מידה ליישם אמצעי אבטחה נוספים.

בנוסף, על ידי מתן עדיפות לאבטחה בכל שלב של פיתוח API, אתה יכול להבטיח את האבטחה של מערכות הקצה שלך.