ارسال داده از یک مکان به مکان دیگر؟ برای آرامش خاطر خود و محافظت از کاربرانتان، باید آن را با JWT ایمن کنید.
هنگامی که در حال ساخت یک برنامه هستید، محافظت از داده های حساس در برابر دسترسی غیرمجاز بسیار مهم است. بسیاری از برنامههای کاربردی مدرن وب، موبایل و ابری از REST API به عنوان ابزار اصلی ارتباط استفاده میکنند. در نتیجه، طراحی و توسعه APIهای Backend با امنیت در خط مقدم بسیار مهم است.
یکی از رویکردهای موثر برای ایمن سازی REST API شامل JSON Web Tokens (JWTs) است. این توکنها مکانیزم قوی برای احراز هویت و مجوز کاربر ارائه میدهند که به محافظت از منابع محافظت شده در برابر دسترسی عوامل مخرب کمک میکند.
توکن های وب JSON چیست؟
JSON Web Token (JWT) یک استاندارد امنیتی پرکاربرد است. این یک روش مختصر و مستقل برای انتقال ایمن داده ها بین یک برنامه مشتری و یک سیستم باطن ارائه می دهد.
یک REST API می تواند از JWT ها برای شناسایی ایمن و احراز هویت کاربران هنگام درخواست HTTP برای دسترسی به منابع محافظت شده استفاده کند.
یک توکن وب JSON از سه بخش مجزا تشکیل شده است: هدر، بارگذاری و امضا. هر قسمت را رمزگذاری می کند و با استفاده از نقطه (“.”) آنها را به هم متصل می کند.
هدر الگوریتم رمزنگاری مورد استفاده برای امضای توکن را توصیف میکند، در حالی که محموله حاوی دادههایی درباره کاربر و هر ابرداده اضافی است.
در نهایت، امضا که با استفاده از هدر، بار و کلید مخفی محاسبه میشود، یکپارچگی و صحت توکن را تضمین میکند.
در حالی که اصول اولیه JWT ها در دسترس نیست، بیایید یک Node.js REST API بسازیم و JWT ها را پیاده سازی کنیم.
یک برنامه Express.js و پایگاه داده MongoDB راه اندازی کنید
در اینجا خواهید فهمید که چگونه یک REST API احراز هویت ساده بسازید که هم عملکرد ثبت نام و هم ورود به سیستم را کنترل می کند. هنگامی که فرآیند ورود به سیستم احراز هویت یک کاربر را تأیید کرد، آنها باید بتوانند درخواست های HTTP را به یک مسیر API محافظت شده ارسال کنند.
شما می توانید کد پروژه را در اینجا پیدا کنید
مخزن GitHub
.
برای شروع، یک وب سرور Express ایجاد کنید و این بسته ها را نصب کنید:
npm install cors dotenv bycrpt mongoose cookie-parser crypto jsonwebtoken mongodb
بعد، یک پایگاه داده MongoDB ایجاد کنید یا یک خوشه MongoDB را روی ابر پیکربندی کنید. سپس رشته اتصال پایگاه داده را کپی کنید، یک فایل .env در دایرکتوری ریشه ایجاد کنید و در رشته اتصال پیست کنید:
CONNECTION_STRING="connection string"
اتصال پایگاه داده را پیکربندی کنید
یک فایل utils/db.js جدید در دایرکتوری ریشه پوشه پروژه خود ایجاد کنید. در این فایل کد زیر را برای برقراری ارتباط پایگاه داده با استفاده از Mongoose اضافه کنید.
const mongoose = require('mongoose');
const connectDB = async () => {
try {
await mongoose.connect(process.env.CONNECTION_STRING);
console.log("Connected to MongoDB!");
} catch (error) {
console.error("Error connecting to MongoDB:", error);
}
};
module.exports = connectDB;
مدل داده را تعریف کنید
با استفاده از Mongoose یک طرح ساده برای داده های کاربر تعریف کنید. در پوشه اصلی، یک فایل model/user.model.js جدید ایجاد کنید و کد زیر را اضافه کنید.
const mongoose = require('mongoose');
const userSchema = new mongoose.Schema({
username: String,
password: {
type: String,
required: true,
unique: true,
},
});
const User = mongoose.model("User", userSchema);
module.exports = User;
کنترلرها را برای مسیرهای API تعریف کنید
توابع کنترل کننده ثبت نام و ورود را مدیریت خواهند کرد. آنها بخش قابل توجهی از این برنامه نمونه هستند. در پوشه اصلی، یک فایل controllers/userControllers.js ایجاد کنید و کد زیر را اضافه کنید:
- تعریف ثبت نام کاربر controller.const User = require(‘../models/user.model’);const bcrypt = require(‘bcrypt’);const {generatortoken } = require(‘../middleware/auth’); exports.registerUser = async (req، res) => { const { نام کاربری، رمز عبور } = req.body; سعی کنید { const hash = await bcrypt.hash(password, 10); await User.create({ username, password: hash }); res.status(201).send({ message: ‘User ثبت نام با موفقیت’ }); } catch (خطا) { console.log(error); res.status(500).send({ message: ‘خطایی رخ داد!!’ }); }} این قطعه کد رمز عبور ارائه شده را با استفاده از bcrypt هش می کند و سپس یک رکورد کاربری جدید در پایگاه داده ایجاد می کند و نام کاربری و رمز عبور هش شده را ذخیره می کند. اگر ثبت نام با موفقیت انجام شود، یک پاسخ با یک پیام موفقیت ارسال می کند.
- یک کنترل کننده ورود به سیستم برای مدیریت فرآیند ورود کاربر تعریف کنید:exports.loginUser = async (req, res) => { const { username, password } = req.body; { const user = await User.findOne({ username }) را امتحان کنید. if (!user) { return res.status(404).send({ message: ‘User not found’ }); } const passwordMatch = await bcrypt.compare(password, user.password); if (!passwordMatch) { return res.status(401).send({ message: ‘Invalid login credentials’ }); } const payload = { userId: user._id }; const token = generateToken(payload); res.cookie(‘token’, token, { httpOnly: true }); res.status(200).json({ message: ‘ورود با موفقیت’}); } catch (خطا) { console.log(error); res.status(500).send({ message: ‘هنگام ورود به سیستم خطایی رخ داد’ }); }} هنگامی که کاربر درخواستی را به مسیر /login ارسال می کند، باید اعتبار احراز هویت خود را در بدنه درخواست ارسال کند. سپس کد آن اعتبارنامه ها را تأیید می کند و یک JSON Web Token ایجاد می کند. رمز به طور ایمن در یک کوکی با پرچم httpOnly روی true ذخیره می شود. این امر از دسترسی جاوا اسکریپت سمت سرویس گیرنده به توکن جلوگیری می کند و در برابر حملات بالقوه اسکریپت بین سایتی (XSS) محافظت می کند.
- در نهایت، یک مسیر محافظت شده تعریف کنید:exports.getUsers = async (req، res) => { try { const users = await User.find({}); res.json(کاربران); } catch (خطا) { console.log(error); res.status(500).send({ message: ‘خطایی رخ داد!!’ }); }} با ذخیره کردن JWT در یک کوکی، درخواستهای API بعدی از کاربر تأیید شده به طور خودکار شامل توکن میشود و به سرور اجازه میدهد تا درخواستها را تأیید و تأیید کند.
const User = require('../models/user.model');
const bcrypt = require('bcrypt');
const { generateToken } = require('../middleware/auth');
exports.registerUser = async (req, res) => {
const { username, password } = req.body;
try {
const hash = await bcrypt.hash(password, 10);
await User.create({ username, password: hash });
res.status(201).send({ message: 'User registered successfully' });
} catch (error) {
console.log(error);
res.status(500).send({ message: 'An error occurred!! ' });
}
};
exports.loginUser = async (req, res) => {
const { username, password } = req.body;
try {
const user = await User.findOne({ username });
if (!user) {
return res.status(404).send({ message: 'User not found' });
}
const passwordMatch = await bcrypt.compare(password, user.password);
if (!passwordMatch) {
return res.status(401).send({ message: 'Invalid login credentials' });
}
const payload = { userId: user._id };
const token = generateToken(payload);
res.cookie('token', token, { httpOnly: true });
res.status(200).json({ message: 'Login successful'});
} catch (error) {
console.log(error);
res.status(500).send({ message: 'An error occurred while logging in' });
}
};
exports.getUsers = async (req, res) => {
try {
const users = await User.find({});
res.json(users);
} catch (error) {
console.log(error);
res.status(500).send({ message: 'An error occurred!!' });
}
};
یک میان افزار احراز هویت ایجاد کنید
اکنون که یک کنترلکننده ورود به سیستم تعریف کردهاید که پس از احراز هویت موفق، یک توکن JWT تولید میکند، توابع احراز هویت میانافزار را تعریف کنید که توکن JWT را تولید و تأیید میکند.
در پوشه ریشه، یک پوشه جدید، میان افزار ایجاد کنید. داخل این پوشه دو فایل auth.js و config.js اضافه کنید.
این کد را به config.js اضافه کنید:
const crypto = require('crypto');
module.exports = {
secretKey: crypto.randomBytes(32).toString('hex')
};
این کد هر بار که اجرا می شود یک کلید مخفی تصادفی جدید تولید می کند. سپس می توانید از این کلید مخفی برای امضا و تأیید صحت JWT ها استفاده کنید. هنگامی که یک کاربر با موفقیت احراز هویت شد، یک JWT را با کلید مخفی ایجاد و امضا کنید. سپس سرور از کلید برای تأیید اعتبار JWT استفاده می کند.
کد زیر را در auth.js اضافه کنید که توابع میانافزاری را تعریف میکند که JWTها را تولید و تأیید میکند.
const jwt = require('jsonwebtoken');
const { secretKey } = require('./config');
const generateToken = (payload) => {
const token = jwt.sign(payload, secretKey, { expiresIn: '1h' });
return token ;
};
const verifyToken = (req, res, next) => {
const token = req.cookies.token;
if (!token) {
return res.status(401).json({ message: 'No token provided' });
}
jwt.verify(token, secretKey, (err, decoded) => {
if (err) {
return res.status(401).json({ message: 'Invalid token' });
}
req.userId = decoded.userId;
next();
});
};
module.exports = { generateToken, verifyToken };
تابعgeneToken با امضای یک بار با استفاده از یک کلید مخفی و تنظیم زمان انقضا، یک JWT تولید می کند، در حالی که تابع verifyToken به عنوان میان افزار برای تأیید صحت و اعتبار یک توکن ارائه شده عمل می کند.
مسیرهای API را تعریف کنید
یک فایل routes/userRoutes.js جدید در پوشه اصلی ایجاد کنید و کد زیر را اضافه کنید.
const express = require('express');
const router = express.Router();
const userControllers = require('../controllers/userControllers');
const { verifyToken } = require('../middleware/auth');
router.post('/api/register', userControllers.registerUser);
router.post('/api/login', userControllers.loginUser);
router.get('/api/users', verifyToken, userControllers.getUsers);
module.exports = router;
نقطه ورودی سرور خود را به روز کنید
فایل server.js خود را با کد زیر به روز کنید.
const express = require('express');
const cors = require('cors');
const app = express();
const port = 5000;
require('dotenv').config();
const connectDB = require('./utils/db');
const cookieParser = require('cookie-parser');
connectDB();
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(cors());
app.use(cookieParser());
const userRoutes = require('./routes/userRoutes');
app.use('/', userRoutes);
app.listen(port, () => {
console.log(`Server is listening at http://localhost:${port}`);
});
برای آزمایش REST API، سرور توسعه را بچرخانید و درخواستهای API را به نقاط پایانی تعریفشده ارسال کنید:
node server.js
ایمن سازی Node.js REST API
ایمن سازی Node.js REST API فراتر از استفاده از JWT است، اگرچه آنها نقش مهمی در احراز هویت و مجوز دارند، اتخاذ یک رویکرد امنیتی جامع برای امنیت برای محافظت از سیستم های باطن شما ضروری است. در کنار JWT ها، شما همچنین باید پیاده سازی HTTPS را برای رمزگذاری ارتباطات، اعتبارسنجی ورودی و پاکسازی و بسیاری موارد دیگر در نظر بگیرید.
با ترکیب چندین اقدامات امنیتی، می توانید یک چارچوب امنیتی قوی برای Node.js REST API های خود ایجاد کنید و خطر دسترسی غیرمجاز، نقض داده ها و سایر تهدیدات امنیتی را به حداقل برسانید.