update
This commit is contained in:
344
LOGIN_GUIDE.md
Normal file
344
LOGIN_GUIDE.md
Normal file
@@ -0,0 +1,344 @@
|
||||
# 🔐 Hướng Dẫn Hệ Thống Đăng Nhập
|
||||
|
||||
## 📋 Mô Tả Quy Trình Đăng Nhập
|
||||
|
||||
### 1️⃣ Kiến Trúc Hệ Thống
|
||||
|
||||
```
|
||||
┌─────────────┐ ┌──────────────┐ ┌──────────────┐
|
||||
│ Browser │────▶│ API Server │────▶│ Database │
|
||||
│ (login.html)│◀────│ (Express) │◀────│ (MySQL) │
|
||||
└─────────────┘ └──────────────┘ └──────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────┐
|
||||
│ Redis │
|
||||
│ (Cache) │
|
||||
└──────────────┘
|
||||
```
|
||||
|
||||
### 2️⃣ Quy Trình Đăng Nhập Chi Tiết
|
||||
|
||||
#### **Bước 1: User Nhập Thông Tin**
|
||||
- Frontend (login.html) hiển thị form đăng nhập
|
||||
- User nhập `username` hoặc `email` và `password`
|
||||
- Click button "Đăng nhập"
|
||||
|
||||
#### **Bước 2: Gửi Request Đến Server**
|
||||
```javascript
|
||||
POST /api/users/login
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"username": "admin", // hoặc email
|
||||
"password": "admin123"
|
||||
}
|
||||
```
|
||||
|
||||
#### **Bước 3: Server Xác Thực**
|
||||
|
||||
**3.1. Tìm User trong Database**
|
||||
```javascript
|
||||
// Tìm user theo username HOẶC email
|
||||
UsersAuth.findOne({
|
||||
where: {
|
||||
OR: [
|
||||
{ username: username },
|
||||
{ email: username }
|
||||
]
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
**3.2. Kiểm Tra Các Điều Kiện**
|
||||
- ❌ User không tồn tại → Trả về lỗi 401
|
||||
- ❌ Tài khoản bị khóa (`is_locked = true`) → Trả về lỗi 403
|
||||
- ❌ Tài khoản không active (`is_active = false`) → Trả về lỗi 403
|
||||
|
||||
**3.3. Verify Password**
|
||||
```javascript
|
||||
// Password được hash theo công thức:
|
||||
// hash = bcrypt(password + salt)
|
||||
|
||||
const passwordMatch = await bcrypt.compare(
|
||||
password + user.salt,
|
||||
user.password_hash
|
||||
);
|
||||
```
|
||||
|
||||
**3.4. Xử Lý Kết Quả**
|
||||
|
||||
**❌ Nếu Password Sai:**
|
||||
- Tăng `failed_login_attempts` lên 1
|
||||
- Nếu thất bại ≥ 5 lần:
|
||||
- Khóa tài khoản (`is_locked = true`)
|
||||
- Set `locked_until` = hiện tại + 30 phút
|
||||
- Trả về lỗi 401
|
||||
|
||||
**✅ Nếu Password Đúng:**
|
||||
- Reset `failed_login_attempts = 0`
|
||||
- Tăng `login_count` lên 1
|
||||
- Cập nhật `last_login` = thời gian hiện tại
|
||||
- Cập nhật `last_login_ip` = IP của client
|
||||
- Tạo `session_id` mới (UUID)
|
||||
|
||||
#### **Bước 4: Tạo JWT Token**
|
||||
```javascript
|
||||
const token = jwt.sign(
|
||||
{
|
||||
userId: user.id,
|
||||
username: user.username,
|
||||
email: user.email,
|
||||
sessionId: sessionId
|
||||
},
|
||||
JWT_SECRET,
|
||||
{ expiresIn: '24h' }
|
||||
);
|
||||
```
|
||||
|
||||
#### **Bước 5: Trả Response Về Client**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Đăng nhập thành công",
|
||||
"data": {
|
||||
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
||||
"user": {
|
||||
"id": "uuid-here",
|
||||
"username": "admin",
|
||||
"email": "admin@senaai.tech",
|
||||
"profile": { ... },
|
||||
"last_login": "2026-01-19T...",
|
||||
"login_count": 42
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### **Bước 6: Client Lưu Token**
|
||||
```javascript
|
||||
// Lưu token vào localStorage
|
||||
localStorage.setItem('token', data.data.token);
|
||||
```
|
||||
|
||||
### 3️⃣ Sử Dụng Token Để Xác Thực
|
||||
|
||||
**Các Request Tiếp Theo:**
|
||||
```javascript
|
||||
fetch('/api/users/profile', {
|
||||
headers: {
|
||||
'Authorization': 'Bearer ' + token
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
**Server Verify Token:**
|
||||
```javascript
|
||||
POST /api/users/verify-token
|
||||
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
|
||||
```
|
||||
|
||||
### 4️⃣ Đăng Xuất
|
||||
|
||||
```javascript
|
||||
POST /api/users/logout
|
||||
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
|
||||
|
||||
// Server sẽ:
|
||||
// 1. Xóa current_session_id trong database
|
||||
// 2. Token sẽ không còn valid nữa
|
||||
```
|
||||
|
||||
## 🔒 Bảo Mật
|
||||
|
||||
### Password Security
|
||||
- **Salt**: Mỗi user có salt riêng (16 bytes random)
|
||||
- **Hash Algorithm**: bcrypt với cost factor = 10
|
||||
- **Formula**: `hash = bcrypt(password + salt, 10)`
|
||||
|
||||
### Token Security
|
||||
- **Algorithm**: JWT với HS256
|
||||
- **Expiration**: 24 giờ
|
||||
- **Secret Key**: Lưu trong environment variable
|
||||
- **Session Tracking**: Mỗi lần login tạo session_id mới
|
||||
|
||||
### Brute Force Protection
|
||||
- Khóa tài khoản sau 5 lần đăng nhập sai
|
||||
- Thời gian khóa: 30 phút
|
||||
- Tự động mở khóa sau khi hết thời gian
|
||||
|
||||
## 📊 Database Schema
|
||||
|
||||
### Table: `users_auth`
|
||||
```sql
|
||||
- id (UUID, PK)
|
||||
- username (VARCHAR, UNIQUE)
|
||||
- email (VARCHAR, UNIQUE)
|
||||
- password_hash (VARCHAR)
|
||||
- salt (VARCHAR)
|
||||
- current_session_id (VARCHAR)
|
||||
- last_login (DATETIME)
|
||||
- last_login_ip (VARCHAR)
|
||||
- login_count (INT)
|
||||
- failed_login_attempts (INT)
|
||||
- is_active (BOOLEAN)
|
||||
- is_locked (BOOLEAN)
|
||||
- locked_until (DATETIME)
|
||||
```
|
||||
|
||||
## 🚀 API Endpoints
|
||||
|
||||
### Authentication
|
||||
| Method | Endpoint | Mô Tả |
|
||||
|--------|----------|-------|
|
||||
| POST | `/api/users/login` | Đăng nhập |
|
||||
| POST | `/api/users/register` | Đăng ký tài khoản mới |
|
||||
| POST | `/api/users/verify-token` | Xác thực token |
|
||||
| POST | `/api/users/logout` | Đăng xuất |
|
||||
|
||||
### User Management
|
||||
| Method | Endpoint | Mô Tả |
|
||||
|--------|----------|-------|
|
||||
| GET | `/api/users` | Lấy danh sách users |
|
||||
| GET | `/api/users/:id` | Lấy thông tin user theo ID |
|
||||
| POST | `/api/users` | Tạo user mới |
|
||||
| PUT | `/api/users/:id` | Cập nhật user |
|
||||
| DELETE | `/api/users/:id` | Xóa user (soft delete) |
|
||||
|
||||
## 🧪 Testing
|
||||
|
||||
### 1. Tạo Test Users
|
||||
```bash
|
||||
node scripts/create-test-user.js
|
||||
```
|
||||
|
||||
### 2. Mở Login Page
|
||||
```
|
||||
http://localhost:4000/login.html
|
||||
```
|
||||
|
||||
### 3. Test Login
|
||||
**Test Accounts:**
|
||||
- **Admin**: `admin` / `admin123`
|
||||
- **Teacher**: `teacher1` / `teacher123`
|
||||
- **Student**: `student1` / `student123`
|
||||
|
||||
### 4. Test Với cURL
|
||||
|
||||
**Login:**
|
||||
```bash
|
||||
curl -X POST http://localhost:4000/api/users/login \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"username":"admin","password":"admin123"}'
|
||||
```
|
||||
|
||||
**Verify Token:**
|
||||
```bash
|
||||
curl -X POST http://localhost:4000/api/users/verify-token \
|
||||
-H "Authorization: Bearer YOUR_TOKEN_HERE"
|
||||
```
|
||||
|
||||
**Logout:**
|
||||
```bash
|
||||
curl -X POST http://localhost:4000/api/users/logout \
|
||||
-H "Authorization: Bearer YOUR_TOKEN_HERE"
|
||||
```
|
||||
|
||||
## 🎯 Luồng Xử Lý (Flow Diagram)
|
||||
|
||||
```
|
||||
User Input
|
||||
│
|
||||
▼
|
||||
[Frontend] login.html
|
||||
│ POST /api/users/login
|
||||
│ { username, password }
|
||||
▼
|
||||
[Backend] userController.login()
|
||||
│
|
||||
├──▶ Tìm user trong DB
|
||||
│ ├─ Không tìm thấy → 401 Unauthorized
|
||||
│ └─ Tìm thấy ✓
|
||||
│
|
||||
├──▶ Kiểm tra is_locked
|
||||
│ ├─ Bị khóa → 403 Forbidden
|
||||
│ └─ Không khóa ✓
|
||||
│
|
||||
├──▶ Kiểm tra is_active
|
||||
│ ├─ Không active → 403 Forbidden
|
||||
│ └─ Active ✓
|
||||
│
|
||||
├──▶ Verify password với bcrypt
|
||||
│ ├─ Sai → Tăng failed_attempts
|
||||
│ │ ├─ ≥5 lần → Khóa tài khoản
|
||||
│ │ └─ 401 Unauthorized
|
||||
│ └─ Đúng ✓
|
||||
│
|
||||
├──▶ Cập nhật thông tin login
|
||||
│ ├─ Reset failed_attempts = 0
|
||||
│ ├─ Tăng login_count
|
||||
│ ├─ Cập nhật last_login
|
||||
│ ├─ Cập nhật last_login_ip
|
||||
│ └─ Tạo session_id mới
|
||||
│
|
||||
├──▶ Tạo JWT token
|
||||
│ └─ Expire sau 24h
|
||||
│
|
||||
└──▶ Return response
|
||||
└─ { token, user }
|
||||
│
|
||||
▼
|
||||
[Frontend] Nhận response
|
||||
│
|
||||
├──▶ Lưu token vào localStorage
|
||||
├──▶ Hiển thị thông báo thành công
|
||||
└──▶ Sử dụng token cho các request sau
|
||||
```
|
||||
|
||||
## 💡 Best Practices
|
||||
|
||||
### Frontend
|
||||
- ✅ Lưu token trong localStorage
|
||||
- ✅ Gửi token trong header: `Authorization: Bearer TOKEN`
|
||||
- ✅ Xử lý token expired (redirect về login)
|
||||
- ✅ Clear token khi logout
|
||||
|
||||
### Backend
|
||||
- ✅ Validate input (username, password)
|
||||
- ✅ Rate limiting để chống brute force
|
||||
- ✅ Log các lần đăng nhập thất bại
|
||||
- ✅ Use HTTPS trong production
|
||||
- ✅ Rotate JWT secret định kỳ
|
||||
|
||||
### Database
|
||||
- ✅ Index trên username, email
|
||||
- ✅ Không bao giờ lưu plain password
|
||||
- ✅ Use UUID cho ID
|
||||
- ✅ Soft delete (is_active) thay vì DELETE
|
||||
|
||||
## 🔍 Troubleshooting
|
||||
|
||||
### "401 Unauthorized"
|
||||
- Kiểm tra username/password có đúng không
|
||||
- Kiểm tra user có tồn tại trong DB không
|
||||
|
||||
### "403 Forbidden - Account Locked"
|
||||
- Tài khoản bị khóa do nhập sai password ≥5 lần
|
||||
- Đợi 30 phút hoặc admin mở khóa
|
||||
|
||||
### "Token Invalid"
|
||||
- Token đã expire (>24h)
|
||||
- Session đã bị logout
|
||||
- Đăng nhập lại để lấy token mới
|
||||
|
||||
## 📚 Dependencies
|
||||
|
||||
- `bcrypt`: Hash passwords
|
||||
- `jsonwebtoken`: Tạo và verify JWT tokens
|
||||
- `crypto`: Generate salt và secrets
|
||||
- `sequelize`: ORM for MySQL
|
||||
- `express`: Web framework
|
||||
|
||||
---
|
||||
|
||||
**Created by Sena Team** 🚀
|
||||
Reference in New Issue
Block a user