Express: kirish va marshrutni himoyalash
Oldingi darsda foydalanuvchi hisob yaratdi va paroli hash holida bazaga tushdi. Endi u qaytib keladi va parolini yozadi.
Server ikki ish qilishi kerak: parol to’g’riligini tekshirish va “bu odam kirgan” degan faktni eslab qolish. Keyin esa ba’zi marshrutlarni faqat kirganlarga ochish.
Kirish marshruti
Parolni solishtirish uchun modelga bitta funksiya qo’shamiz.
models/user.js:
function verifyPassword(password, passwordHash) {
return bcrypt.compare(password, passwordHash);
}
function findById(id) {
const row = database.prepare("SELECT * FROM users WHERE id = ?").get(id);
return row ? toUser(row) : null;
}
module.exports = { findByEmail, findById, create, verifyPassword, toUser };
bcrypt.compare yozilgan parolni bazadagi hash bilan solishtiradi.
Hash’dan parolni qaytarib olmaydi — kiritilgan parolni o’sha salt
bilan qayta hash qilib, natijalarni tekshiradi.
Endi marshrut. routes/auth.js:
router.post("/login", async (request, response, next) => {
try {
const email =
typeof request.body.email === "string" ? request.body.email.trim() : "";
const password =
typeof request.body.password === "string" ? request.body.password : "";
const row = User.findByEmail(email);
// Foydalanuvchi topilmasa ham parol tekshiruvini o'tkazamiz
const isValid = row
? await User.verifyPassword(password, row.password_hash)
: false;
if (!isValid) {
return response.status(401).json({ error: "Email yoki parol noto'g'ri" });
}
// Sessiya identifikatorini yangilaymiz
request.session.regenerate((error) => {
if (error) {
return next(error);
}
request.session.userId = row.id;
response.json(User.toUser(row));
});
} catch (error) {
next(error);
}
});
curl -i -c session.txt -X POST http://localhost:3000/login -H "Content-Type: application/json" -d '{"email":"nodira@example.com","password":"kuchli-parol-123"}'
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{"id":2,"email":"nodira@example.com","createdAt":"2026-09-12 14:09:21"}
-c session.txt sessiya cookie’sini saqladi. Endi server bu curl ni
taniydi.
Xato xabari umumiy bo’lishi kerak
Marshrutda bitta xabar ishlatildi: "Email yoki parol noto'g'ri".
Aniqroq yozish vasvasasi kuchli, lekin bunday qilinmaydi.
Tekshirib ko’ring. Mavjud email, xato parol:
curl -i -X POST http://localhost:3000/login -H "Content-Type: application/json" -d '{"email":"nodira@example.com","password":"xato-parol-999"}'
HTTP/1.1 401 Unauthorized
Content-Type: application/json; charset=utf-8
{"error":"Email yoki parol noto'g'ri"}
Mavjud bo’lmagan email:
curl -i -X POST http://localhost:3000/login -H "Content-Type: application/json" -d '{"email":"yoq@example.com","password":"xato-parol-999"}'
HTTP/1.1 401 Unauthorized
Content-Type: application/json; charset=utf-8
{"error":"Email yoki parol noto'g'ri"}
Javoblar ayni bir xil. Tashqaridan qaraganda ikki holatni ajratib bo’lmaydi.
session.regenerate nima uchun
Kirish paytida sessiya id si yangilandi. Bu bitta qatorli, lekin muhim
himoya.
regenerate callback qabul qiladi, shuning uchun userId va javob uning
ichida yoziladi. Tashqarida yozsangiz, ular sessiya yangilanishidan
oldin bajarilib, qiymat yo’qoladi.
requireAuth middleware
Endi marshrutlarni himoyalaymiz. middleware/auth.js:
// middleware/auth.js
function requireAuth(request, response, next) {
if (!request.session.userId) {
return response.status(401).json({ error: "Avval tizimga kiring" });
}
next();
}
module.exports = { requireAuth };
Bu middleware darsidagi qolipning to’g’ridan-to’g’ri
qo’llanishi: shart bajarilsa next(), bajarilmasa javob va zanjir
to’xtaydi.
app.js da ulash:
const { requireAuth } = require("./middleware/auth");
app.use(express.json());
app.use(session({ /* sozlamalar */ }));
// Butun tasks API'si himoyalandi
app.use("/api/tasks", requireAuth, tasksRouter);
Kirmasdan so’rang:
curl -i http://localhost:3000/api/tasks
HTTP/1.1 401 Unauthorized
Content-Type: application/json; charset=utf-8
{"error":"Avval tizimga kiring"}
Kirgandan keyin:
curl -i -b session.txt http://localhost:3000/api/tasks
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
[{"id":1,"title":"Express o'rganish","isDone":true,"createdAt":"2026-09-12 14:04:16"}]
Bitta middleware butun Router ni qopladi — beshta marshrutning har
biriga alohida yozish shart emas.
401 va 403 farqi
Ikki kod chalkashtiriladi, lekin ular boshqa savolga javob beradi.
| Kod | Ma’nosi | Misol |
|---|---|---|
401 Unauthorized |
Kim ekaningiz noma’lum | Kirmagan foydalanuvchi |
403 Forbidden |
Kim ekaningiz ma’lum, huquqingiz yo’q | Oddiy foydalanuvchi admin sahifasida |
401 — “kiring”. 403 — “kirdingiz, lekin bu sizga emas”. Frontend
ularni turlicha ishlaydi: birinchisida kirish sahifasiga yo’naltiradi,
ikkinchisida “ruxsat yo’q” deb ko’rsatadi.
O’z ma’lumotini olish
Frontend “kim kirgan?” deb so’raydigan marshrut odatiy:
router.get("/me", (request, response) => {
if (!request.session.userId) {
return response.status(401).json({ error: "Avval tizimga kiring" });
}
response.json(User.findById(request.session.userId));
});
curl -b session.txt http://localhost:3000/me
{"id":2,"email":"nodira@example.com","createdAt":"2026-09-12 14:09:21"}
Sessiyada faqat userId saqlanadi, qolgan ma’lumot bazadan olinadi. Nega
butun foydalanuvchi obyektini sessiyaga solmaymiz: email o’zgarsa yoki
hisob o’chirilsa, sessiyadagi nusxa eskirib qoladi. id esa hech qachon
o’zgarmaydi.
HTML sahifalar uchun himoya
API 401 JSON qaytaradi. Brauzer sahifasi uchun bu noqulay — foydalanuvchi
xom JSON ko’rmasligi kerak. Ikkinchi variant yoziladi:
function requireAuthPage(request, response, next) {
if (!request.session.userId) {
return response.redirect("/login");
}
next();
}
module.exports = { requireAuth, requireAuthPage };
app.get("/dashboard", requireAuthPage, (request, response) => {
response.render("dashboard", { pageTitle: "Mening vazifalarim" });
});
Farq bitta qatorda: JSON javob o’rniga yo’naltirish. Mantiq bir xil.
Chiqish
router.post("/logout", (request, response, next) => {
request.session.destroy((error) => {
if (error) {
return next(error);
}
response.clearCookie("connect.sid");
response.json({ message: "Chiqdingiz" });
});
});
curl -b session.txt -X POST http://localhost:3000/logout
{"message":"Chiqdingiz"}
curl -i -b session.txt http://localhost:3000/me
HTTP/1.1 401 Unauthorized
Content-Type: application/json; charset=utf-8
{"error":"Avval tizimga kiring"}
Eski cookie hamon curl da turibdi, lekin serverda sessiya yo’q — javob
401.
Ma’lumotni foydalanuvchiga bog’lash
Hozir kirgan har kim hamma vazifani ko’radi. To’liq tizimda har vazifa o’z egasiga tegishli bo’ladi.
Buning uchun yangi migratsiya kerak:
// migrations/004-add-task-user.js
module.exports = {
name: "004-add-task-user",
up(database) {
database.exec(
"ALTER TABLE tasks ADD COLUMN user_id INTEGER REFERENCES users(id)"
);
},
};
Model funksiyalari userId ni qabul qiladigan bo’ladi:
function getAllByUser(userId) {
const rows = database
.prepare("SELECT * FROM tasks WHERE user_id = ? ORDER BY id")
.all(userId);
return rows.map(toTask);
}
Marshrut esa sessiyadan oladi:
router.get("/", (request, response) => {
response.json(Task.getAllByUser(request.session.userId));
});
Amaliy mashq
task-app loyihasida davom eting:
models/user.jsgaverifyPasswordvafindByIdni qo’shing;POST /loginni yozib, to’g’ri parol bilan200va sessiya cookie olishingizni tasdiqlang;- Xato parol va mavjud bo’lmagan email bilan sinab, ikkala javob ham aynan bir xil ekanini tekshiring;
requireAuthmiddleware’ini yozib,/api/tasksga ulang va kirmasdan401olishingizni ko’ring;session()middleware’inirequireAuthdan pastga ko’chiring, chiqqan xatoni o’qing, so’ng joyiga qaytaring;POST /logoutni yozib, chiqqandan keyin/me401berishini tasdiqlang.
3-qadam darsning asosiy xavfsizlik g’oyasi: ikki javob farq qilsa, saytingiz foydalanuvchi ro’yxatini o’zi aytib beradi.
Qisqacha xulosa
bcrypt.comparekiritilgan parolni hash bilan solishtiradi; asl parol hech qayerda saqlanmaydi.- Email topilmasa ham, parol xato bo’lsa ham — bitta umumiy xabar qaytariladi.
session.regenerate()kirish paytida sessiyaidsini yangilab, sessiya fixation hujumini to’sadi.- Sessiyada faqat
userIdsaqlanadi, qolgani bazadan o’qiladi. requireAuth—next()chaqiruvchi oddiy middleware; uniRouteroldiga qo’yib butun guruhni himoyalash mumkin.401— kim ekaningiz noma’lum,403— huquqingiz yo’q; chiqish esaPOSTbilan bajarilib,session.destroy()ni chaqiradi.requireAuthegalikni tekshirmaydi — yozuv kimniki ekanini so’rovda alohida tekshiring.
Keyingi darsda JWT va API token — sessiyasiz autentifikatsiya va mobil ilovalar uchun yondashuv.