TypeScript 中的 OAuth 2.0 和 JWT 简介
\n\n在现代 Web 开发中,安全的用户授权是任何应用程序的基础。两个最流行的标准——OAuth 2.0 和 JWT(JSON Web Token)——使得实现可靠的认证和访问控制系统成为可能。在本文中,我们将探讨如何结合这些技术,使用 TypeScript 从零开始创建一个安全的 API。
\n\n本文面向已经熟悉 TypeScript 基础并希望了解授权在实际中如何工作的初学者开发者。我们不会深入复杂的理论,而是专注于具体的代码示例。
\n\n什么是 OAuth 2.0?
\nOAuth 2.0 是一种授权协议,允许第三方应用程序在无需传递用户密码的情况下,获得对用户资源的有限访问权限。想象一下,您想通过 Google 登录一个网站:您不会将 Google 密码提供给该网站,而只是允许它获取您的电子邮件和姓名。这就是 OAuth 2.0。
\n\n什么是 JWT?
\nJWT(JSON Web Token)是一种紧凑且自包含的令牌格式,它以加密形式包含所有必要的用户信息。令牌由三部分组成:头部、有效载荷(payload)和签名。由于签名的存在,服务器可以确信令牌未被篡改。
\n\n1. 项目设置和依赖安装
\n\n首先,我们创建一个新的 TypeScript 项目。打开终端并执行以下命令:
\n\nmkdir oauth-jwt-ts\ncd oauth-jwt-ts\nnpm init -y\nnpm install express jsonwebtoken bcrypt dotenv cors\nnpm install -D typescript ts-node @types/express @types/jsonwebtoken @types/bcrypt @types/cors @types/node nodemon\n\n创建一个包含基本配置的 tsconfig.json 文件:
{\n "compilerOptions": {\n "target": "ES2020",\n "module": "commonjs",\n "outDir": "./dist",\n "rootDir": "./src",\n "strict": true,\n "esModuleInterop": true,\n "skipLibCheck": true,\n "forceConsistentCasingInFileNames": true\n },\n "include": ["src/**/*"],\n "exclude": ["node_modules"]\n}\n\n文件夹结构如下:
\n- \n
src/— 源代码 \n src/controllers/— 控制器 \n src/middleware/— 用于令牌验证的中间件 \n src/models/— 数据模型 \n src/routes/— 路由 \n .env— 环境变量 \n
2. 使用 JWT 实现基本认证
\n\n我们从创建一个使用 JWT 的简单注册和登录机制开始。创建一个 src/config.ts 文件来存储常量:
import dotenv from 'dotenv';\ndotenv.config();\n\nexport const config = {\n jwtSecret: process.env.JWT_SECRET || 'your-secret-key',\n jwtExpiresIn: process.env.JWT_EXPIRES_IN || '15m',\n refreshTokenExpiresIn: process.env.REFRESH_TOKEN_EXPIRES_IN || '7d',\n port: process.env.PORT || 3000\n};\n\n现在创建一个用于验证 JWT 令牌的中间件。文件 src/middleware/auth.middleware.ts:
import { Request, Response, NextFunction } from 'express';\nimport jwt from 'jsonwebtoken';\nimport { config } from '../config';\n\nexport interface AuthRequest extends Request {\n user?: { userId: string; email: string };\n}\n\nexport const authenticateToken = (req: AuthRequest, res: Response, next: NextFunction) => {\n const authHeader = req.headers['authorization'];\n const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN\n\n if (!token) {\n return res.status(401).json({ message: '未提供令牌' });\n }\n\n try {\n const decoded = jwt.verify(token, config.jwtSecret) as { userId: string; email: string };\n req.user = decoded;\n next