OAuth 2.0 是由 IETF 于 2012 年发布的授权框架,允许第三方应用在用户授权下访问受保护资源。JWT 是基于 JSON 的开放标准(RFC 7519),用于在各方之间安全传输声明。两者常结合使用,构成现代 Web 安全体系的核心。
OAuth 2.0 的核心定位是 授权委托,JWT 的核心定位是 安全信息传递。它们提供了:
┌─────────┐ ┌──────────┐ ┌──────────┐ ┌─────────┐
│ 用户 │ │ 客户端 │ │ 授权服务器│ │ 资源服务器│
│(Resource│ │ (Client) │ │(Auth │ │(Resource│
│ Owner) │ │ │ │ Server) │ │ Server) │
└────┬────┘ └────┬─────┘ └────┬─────┘ └────┬────┘
│ │ │ │
│ 1.请求授权 │ │ │
│───────────────>│ │ │
│ │ │ │
│ 2.重定向到授权服务器 │ │
│<───────────────│ │ │
│ │ │ │
│ 3.用户授权同意 │ │ │
│───────────────>│ │ │
│ │ │ │
│ 4.返回授权码 │ │ │
│<───────────────│ │ │
│ │ │ │
│ │ 5.用授权码换取令牌 │
│ │───────────────>│ │
│ │ │ │
│ │ 6.返回 Access Token │
│ │<───────────────│ │
│ │ │ │
│ │ 7.用 Access Token 请求资源 │
│ │───────────────────────────────>│
│ │ │ │
│ │ 8.返回受保护资源 │
│ │<───────────────────────────────│
│ │ │ │
// JWT 格式:xxxxx.yyyyy.zzzzz
// Header(头部)
{
"alg": "HS256",
"typ": "JWT"
}
// Payload(载荷)
{
"sub": "1234567890",
"name": "Alice",
"iat": 1516239022,
"exp": 1516242622,
"email": "alice@example.com",
"role": "admin"
}
// Signature(签名)
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret
)
// 完整 JWT 示例
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkFsaWNlIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE1MTYyNDI2MjIsImVtYWlsIjoiYWxpY2VAZXhhbXBsZS5jb20iLCJyb2xlIjoiYWRtaW4ifQ.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
// 使用 jsonwebtoken 库
const jwt = require("jsonwebtoken");
const express = require("express");
const app = express();
const SECRET_KEY = "your-secret-key-here";
// 生成 JWT
function generateToken(user) {
const payload = {
id: user.id,
email: user.email,
role: user.role
};
return jwt.sign(payload, SECRET_KEY, { expiresIn: "7d" });
}
// 验证 JWT 中间件
function authenticateToken(req, res, next) {
const authHeader = req.headers["authorization"];
const token = authHeader && authHeader.split(" ")[1];
if (!token) {
return res.status(401).json({ error: "未提供认证令牌" });
}
jwt.verify(token, SECRET_KEY, (err, user) => {
if (err) {
return res.status(403).json({ error: "无效或过期的令牌" });
}
req.user = user;
next();
});
}
// 登录接口
app.post("/login", (req, res) => {
const { email, password } = req.body;
// 验证用户名密码(实际应从数据库查询)
if (email === "alice@example.com" && password === "123456") {
const user = { id: 1, email, role: "admin" };
const token = generateToken(user);
res.json({ token, user });
} else {
res.status(401).json({ error: "用户名或密码错误" });
}
});
// 受保护接口
app.get("/api/users/me", authenticateToken, (req, res) => {
res.json({ user: req.user });
});
// 刷新令牌
app.post("/api/auth/refresh", authenticateToken, (req, res) => {
const user = req.user;
const newToken = generateToken(user);
res.json({ token: newToken });
});
// 使用 firebase/php-jwt 库
require_once "vendor/autoload.php";
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
$secretKey = "your-secret-key-here";
// 生成 JWT
function generateToken($user) {
global $secretKey;
$payload = [
"id" => $user["id"],
"email" => $user["email"],
"role" => $user["role"],
"iat" => time(),
"exp" => time() + (7 * 24 * 60 * 60) // 7 天
];
return JWT::encode($payload, $secretKey, "HS256");
}
// 验证 JWT
function authenticateToken() {
global $secretKey;
$headers = getallheaders();
$authHeader = $headers["Authorization"] ?? "";
if (!preg_match("/Bearer\s+(.*)/", $authHeader, $matches)) {
http_response_code(401);
echo json_encode(["error" => "未提供认证令牌"]);
exit;
}
$token = $matches[1];
try {
$decoded = JWT::decode($token, new Key($secretKey, "HS256"));
return (array) $decoded;
} catch (Exception $e) {
http_response_code(403);
echo json_encode(["error" => "无效或过期的令牌"]);
exit;
}
}
// 登录接口
if ($_SERVER["REQUEST_METHOD"] === "POST" && $_SERVER["REQUEST_URI"] === "/login") {
$data = json_decode(file_get_contents("php://input"), true);
if ($data["email"] === "alice@example.com" && $data["password"] === "123456") {
$user = ["id" => 1, "email" => $data["email"], "role" => "admin"];
$token = generateToken($user);
echo json_encode(["token" => $token, "user" => $user]);
} else {
http_response_code(401);
echo json_encode(["error" => "用户名或密码错误"]);
}
exit;
}
// 受保护接口
if ($_SERVER["REQUEST_METHOD"] === "GET" && $_SERVER["REQUEST_URI"] === "/api/users/me") {
$user = authenticateToken();
echo json_encode(["user" => $user]);
exit;
}
# 使用 PyJWT 库
import jwt
import datetime
from flask import Flask, request, jsonify
app = Flask(__name__)
SECRET_KEY = "your-secret-key-here"
# 生成 JWT
def generate_token(user):
payload = {
"id": user["id"],
"email": user["email"],
"role": user["role"],
"iat": datetime.datetime.utcnow(),
"exp": datetime.datetime.utcnow() + datetime.timedelta(days=7)
}
return jwt.encode(payload, SECRET_KEY, algorithm="HS256")
# 验证 JWT 装饰器
def authenticate_token(f):
def wrapper(*args, **kwargs):
auth_header = request.headers.get("Authorization")
if not auth_header or not auth_header.startswith("Bearer "):
return jsonify({"error": "未提供认证令牌"}), 401
token = auth_header.split(" ")[1]
try:
user = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
request.user = user
except jwt.ExpiredSignatureError:
return jsonify({"error": "令牌已过期"}), 403
except jwt.InvalidTokenError:
return jsonify({"error": "无效的令牌"}), 403
return f(*args, **kwargs)
return wrapper
@app.route("/login", methods=["POST"])
def login():
data = request.json
if data["email"] == "alice@example.com" and data["password"] == "123456":
user = {"id": 1, "email": data["email"], "role": "admin"}
token = generate_token(user)
return jsonify({"token": token, "user": user})
return jsonify({"error": "用户名或密码错误"}), 401
@app.route("/api/users/me", methods=["GET"])
@authenticate_token
def get_current_user():
return jsonify({"user": request.user})
if __name__ == "__main__":
app.run(debug=True)
// 前端重定向到授权服务器
const authUrl = "https://auth.example.com/authorize?" +
"response_type=code" +
"&client_id=your_client_id" +
"&redirect_uri=https://yourapp.com/callback" +
"&scope=openid%20profile%20email" +
"&state=random_state_string";
window.location.href = authUrl;
// 回调处理(后端)
app.get("/callback", async (req, res) => {
const { code, state } = req.query;
// 验证 state(防 CSRF)
if (state !== sessionState) {
return res.status(400).json({ error: "无效的 state" });
}
// 用授权码换取 Access Token
const tokenResponse = await fetch("https://auth.example.com/token", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
grant_type: "authorization_code",
code: code,
redirect_uri: "https://yourapp.com/callback",
client_id: "your_client_id",
client_secret: "your_client_secret"
})
});
const tokens = await tokenResponse.json();
// tokens 包含 access_token, refresh_token, id_token
// 使用 Access Token 获取用户信息
const userInfo = await fetch("https://auth.example.com/userinfo", {
headers: { "Authorization": `Bearer ${tokens.access_token}` }
});
const user = await userInfo.json();
// 创建本地 session 或 JWT
res.redirect("/dashboard");
});
OAuth 2.0 核心概念、四种授权模式、JWT 结构
JWT 生成与验证(多语言)、OAuth 2.0 授权码流程
PKCE、刷新令牌、SSO、OpenID Connect
安全配置、漏洞防护、生产环境最佳实践
OAuth 2.0 & JWT 是现代 Web 安全的"通行证"。
OAuth 2.0 解决了 授权委托 的问题,JWT 解决了 安全信息传递 的问题。两者结合,构成了现代 API 安全、微服务认证、第三方登录的基础。
无论你是前端、后端还是 DevOps 工程师,OAuth 2.0 和 JWT 都是必须掌握的认证授权技术。
"OAuth 2.0 & JWT 是互联网安全的"护照"。" 🛡️