Appearance
认证和授权源代码导览
JWT 源代码分析
jsonwebtoken 核心组件
jsonwebtoken 的源代码结构如下:
jsonwebtoken/
├── lib/
│ ├── index.js # 主入口
│ ├── sign.js # 签名功能
│ ├── verify.js # 验证功能
│ ├── decode.js # 解码功能
│ └── utils.js # 工具函数
├── test/ # 测试文件
└── package.jsonJWT 签名
javascript
// sign.js 核心逻辑
function sign(payload, secretOrPrivateKey, options) {
// 默认选项
const opts = {
algorithm: 'HS256',
expiresIn: '1h',
...options
};
// 创建头部
const header = {
alg: opts.algorithm,
typ: 'JWT'
};
// 编码头部和载荷
const encodedHeader = base64UrlEncode(JSON.stringify(header));
const encodedPayload = base64UrlEncode(JSON.stringify(payload));
// 创建签名数据
const data = `${encodedHeader}.${encodedPayload}`;
// 生成签名
let signature;
if (opts.algorithm.startsWith('HS')) {
// HMAC 签名
const crypto = require('crypto');
const hmac = crypto.createHmac(
opts.algorithm.replace('HS', 'SHA'),
secretOrPrivateKey
);
hmac.update(data);
signature = base64UrlEncode(hmac.digest('base64'));
} else if (opts.algorithm.startsWith('RS')) {
// RSA 签名
const sign = crypto.createSign(opts.algorithm.replace('RS', 'SHA'));
sign.update(data);
signature = base64UrlEncode(sign.sign(secretOrPrivateKey, 'base64'));
}
// 返回完整的 JWT
return `${data}.${signature}`;
}
// Base64 URL 编码
function base64UrlEncode(str) {
return Buffer.from(str)
.toString('base64')
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=/g, '');
}JWT 验证
javascript
// verify.js 核心逻辑
function verify(token, secretOrPublicKey, options) {
// 分割 JWT
const parts = token.split('.');
if (parts.length !== 3) {
throw new Error('Invalid token format');
}
const [encodedHeader, encodedPayload, signature] = parts;
// 解码头部和载荷
const header = JSON.parse(base64UrlDecode(encodedHeader));
const payload = JSON.parse(base64UrlDecode(encodedPayload));
// 验证算法
const opts = { algorithms: ['HS256', 'RS256'], ...options };
if (!opts.algorithms.includes(header.alg)) {
throw new Error('Invalid algorithm');
}
// 验证签名
const data = `${encodedHeader}.${encodedPayload}`;
let isValid = false;
if (header.alg.startsWith('HS')) {
// HMAC 验证
const crypto = require('crypto');
const hmac = crypto.createHmac(
header.alg.replace('HS', 'SHA'),
secretOrPublicKey
);
hmac.update(data);
const expectedSignature = base64UrlEncode(hmac.digest('base64'));
isValid = crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
} else if (header.alg.startsWith('RS')) {
// RSA 验证
const verify = crypto.createVerify(header.alg.replace('RS', 'SHA'));
verify.update(data);
isValid = verify.verify(secretOrPublicKey, signature, 'base64');
}
if (!isValid) {
throw new Error('Invalid signature');
}
// 验证过期时间
if (payload.exp && Date.now() >= payload.exp * 1000) {
throw new Error('Token expired');
}
// 验证签发时间
if (payload.nbf && Date.now() < payload.nbf * 1000) {
throw new Error('Token not yet valid');
}
// 验证签发者
if (opts.issuer && payload.iss !== opts.issuer) {
throw new Error('Invalid issuer');
}
// 验证受众
if (opts.audience && payload.aud !== opts.audience) {
throw new Error('Invalid audience');
}
return payload;
}
// Base64 URL 解码
function base64UrlDecode(str) {
// 添加填充
let padded = str;
while (padded.length % 4) {
padded += '=';
}
// 替换字符
const base64 = padded
.replace(/-/g, '+')
.replace(/_/g, '/');
return Buffer.from(base64, 'base64').toString();
}OAuth 2.0 源代码分析
oauth2-server 核心组件
oauth2-server 的源代码结构如下:
oauth2-server/
├── lib/
│ ├── models/
│ │ ├── access-token.js # 访问令牌模型
│ │ ├── refresh-token.js # 刷新令牌模型
│ │ ├── client.js # 客户端模型
│ │ └── user.js # 用户模型
│ ├── grants/
│ │ ├── authorization-code.js # 授权码模式
│ │ ├── password.js # 密码模式
│ │ ├── client-credentials.js # 客户端凭证模式
│ │ └── refresh-token.js # 刷新令牌模式
│ ├── handlers/
│ │ ├── authorize.js # 授权处理器
│ │ ├── token.js # 令牌处理器
│ │ └── authenticate.js # 认证处理器
│ └── utils.js # 工具函数
├── test/ # 测试文件
└── package.json授权码模式
javascript
// grants/authorization-code.js 核心逻辑
class AuthorizationCodeGrant {
constructor(model) {
this.model = model;
}
// 处理授权请求
async handleAuthorizationRequest(req) {
// 验证客户端
const client = await this.model.getClient(
req.body.client_id,
req.body.client_secret
);
if (!client) {
throw new Error('Invalid client');
}
// 验证用户
const user = await this.model.getUser(
req.body.username,
req.body.password
);
if (!user) {
throw new Error('Invalid user');
}
// 验证重定向 URI
if (!this.validateRedirectUri(client, req.body.redirect_uri)) {
throw new Error('Invalid redirect URI');
}
// 验证权限范围
const scope = this.validateScope(req.body.scope, client.scopes);
if (!scope) {
throw new Error('Invalid scope');
}
// 生成授权码
const authorizationCode = await this.model.saveAuthorizationCode({
code: this.generateAuthorizationCode(),
client_id: client.id,
user_id: user.id,
redirect_uri: req.body.redirect_uri,
scope: scope,
expires_at: this.getExpirationDate(10 * 60) // 10 分钟
});
// 返回授权码
return {
code: authorizationCode.code,
state: req.body.state
};
}
// 处理令牌请求
async handleTokenRequest(req) {
// 验证客户端
const client = await this.model.getClient(
req.body.client_id,
req.body.client_secret
);
if (!client) {
throw new Error('Invalid client');
}
// 验证授权码
const authCode = await this.model.getAuthorizationCode(req.body.code);
if (!authCode) {
throw new Error('Invalid authorization code');
}
// 验证授权码是否过期
if (authCode.expires_at < Date.now()) {
throw new Error('Authorization code expired');
}
// 验证重定向 URI
if (authCode.redirect_uri !== req.body.redirect_uri) {
throw new Error('Redirect URI mismatch');
}
// 验证客户端 ID
if (authCode.client_id !== client.id) {
throw new Error('Client ID mismatch');
}
// 生成访问令牌
const accessToken = this.generateToken({
sub: authCode.user_id,
client_id: client.id,
scope: authCode.scope
});
// 生成刷新令牌
const refreshToken = this.generateToken({
sub: authCode.user_id,
client_id: client.id,
scope: authCode.scope
});
// 保存令牌
await this.model.saveToken({
access_token: accessToken,
refresh_token: refreshToken,
client_id: client.id,
user_id: authCode.user_id,
scope: authCode.scope,
expires_at: this.getExpirationDate(3600) // 1 小时
});
// 删除授权码
await this.model.revokeAuthorizationCode(authCode.code);
// 返回令牌
return {
access_token: accessToken,
refresh_token: refreshToken,
token_type: 'Bearer',
expires_in: 3600,
scope: authCode.scope
};
}
// 生成授权码
generateAuthorizationCode() {
const crypto = require('crypto');
return crypto.randomBytes(32).toString('base64');
}
// 生成令牌
generateToken(payload) {
const jwt = require('jsonwebtoken');
return jwt.sign(payload, process.env.JWT_SECRET, {
expiresIn: '1h'
});
}
// 获取过期时间
getExpirationDate(seconds) {
return new Date(Date.now() + seconds * 1000);
}
// 验证重定向 URI
validateRedirectUri(client, redirectUri) {
return client.redirect_uris.includes(redirectUri);
}
// 验证权限范围
validateScope(requestedScope, allowedScopes) {
const requested = requestedScope ? requestedScope.split(' ') : [];
return requested.filter(scope => allowedScopes.includes(scope)).join(' ');
}
}认证中间件源代码
JWT 中间件
javascript
// middleware/jwt.js 核心逻辑
function jwtMiddleware(options = {}) {
return function (req, res, next) {
// 提取令牌
let token = null;
// 从 Authorization 头提取
const authHeader = req.headers.authorization;
if (authHeader && authHeader.startsWith('Bearer ')) {
token = authHeader.substring(7);
}
// 从 Cookie 提取
if (!token && req.cookies && req.cookies.token) {
token = req.cookies.token;
}
// 从查询参数提取
if (!token && req.query.token) {
token = req.query.token;
}
// 没有令牌
if (!token) {
return res.status(401).json({
error: 'Unauthorized',
message: 'No token provided'
});
}
try {
// 验证令牌
const jwt = require('jsonwebtoken');
const decoded = jwt.verify(token, options.secret || process.env.JWT_SECRET);
// 附加用户信息到请求
req.user = decoded;
req.token = token;
next();
} catch (err) {
// 令牌无效或过期
return res.status(401).json({
error: 'Unauthorized',
message: err.message
});
}
};
}权限中间件
javascript
// middleware/permission.js 核心逻辑
function permissionMiddleware(requiredPermission) {
return async function (req, res, next) {
// 检查用户是否认证
if (!req.user) {
return res.status(401).json({
error: 'Unauthorized',
message: 'Authentication required'
});
}
// 查询用户权限
const userPermissions = await getUserPermissions(req.user.id);
// 检查是否有所需权限
if (!userPermissions.includes(requiredPermission)) {
return res.status(403).json({
error: 'Forbidden',
message: 'Insufficient permissions'
});
}
next();
};
// 获取用户权限
async function getUserPermissions(userId) {
// 从数据库查询用户权限
const { User } = require('../models/user');
const user = await User.findById(userId).populate('role');
return user.role.permissions;
}
}密码哈希源代码
bcrypt 实现
javascript
// utils/password.js 核心逻辑
const bcrypt = require('bcrypt');
const SALT_ROUNDS = 10;
// 哈希密码
async function hashPassword(password) {
const salt = await bcrypt.genSalt(SALT_ROUNDS);
const hash = await bcrypt.hash(password, salt);
return hash;
}
// 验证密码
async function verifyPassword(password, hash) {
const isValid = await bcrypt.compare(password, hash);
return isValid;
}
// 生成随机密码
function generateRandomPassword(length = 12) {
const crypto = require('crypto');
const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*';
let password = '';
for (let i = 0; i < length; i++) {
const randomIndex = crypto.randomInt(0, chars.length);
password += chars[randomIndex];
}
return password;
}
module.exports = {
hashPassword,
verifyPassword,
generateRandomPassword
};总结
JWT 和 OAuth 2.0 的源代码展示了不同的认证授权机制。JWT 使用签名和验证机制,提供了无状态的认证方案;OAuth 2.0 使用授权码和令牌交换机制,提供了安全的第三方授权方案。理解两种方案的源代码结构,有助于我们更好地实现和扩展认证授权功能。
