二、JWT 实现
2.1 Node.js 实现
import jwt from 'jsonwebtoken';
import crypto from 'crypto';
function generateToken(userId) {
const payload = {
sub: userId,
iat: Date.now(),
exp: Date.now() + 24 * 60 * 60 * 1000
};
return jwt.sign(payload, process.env.JWT_SECRET, {
algorithm: 'HS256'
});
}
function verifyToken(token) {
try {
return jwt.verify(token, process.env.JWT_SECRET);
} catch (error) {
if (error.name === 'TokenExpiredError') {
throw new Error('Token expired');
}
throw new Error('Invalid token');
}
}
2.2 使用非对称加密
import jwt from 'jsonwebtoken';
import fs from 'fs';
const privateKey = fs.readFileSync('private.key');
const publicKey = fs.readFileSync('public.key');
const token = jwt.sign(payload, privateKey, { algorithm: 'RS256' });
const decoded = jwt.verify(token, publicKey, { algorithms: ['RS256'] });
三、JWT 安全最佳实践
3.1 短过期时间
const accessToken = jwt.sign(payload, secret, { expiresIn: '15m' });
const refreshToken = jwt.sign(payload, refreshSecret, { expiresIn: '7d' });
3.2 使用 HTTPS
res.cookie('token', token, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'strict',
maxAge: 15 * 60 * 1000
});
3.3 防止敏感信息
const payload = {
userId: user.id,
role: user.role
};
四、OAuth 2.0 流程
4.1 四种授权类型
| 类型 | 场景 |
|---|
| Authorization Code | Web 应用(服务端渲染) |
| Client Credentials | 机器对机器(M2M) |
| Implicit | SPA(不推荐) |
| Password | 高度信任的应用 |
4.2 Authorization Code Flow
(1) 用户点击登录
→ 重定向到 /authorize?response_type=code&client_id=...
(2) 用户授权
→ 重定向回 redirect_uri?code=xxx
(3) 后端换 token
→ POST /token (code + client_secret)
→ 返回 access_token + refresh_token
五、OAuth 2.0 实现
5.1 Node.js 简单服务器
import express from 'express';
import jwt from 'jsonwebtoken';
const app = express();
const users = new Map();
app.get('/authorize', (req, res) => {
const { client_id, redirect_uri, response_type } = req.query;
res.send(`
<form method="POST" action="/approve">
<input type="hidden" name="redirect_uri" value="${redirect_uri}">
<button type="submit">授权</button>
</form>
`);
});
app.post('/approve', (req, res) => {
const authCode = 'random-code';
authCodes.set(authCode, { userId: 1, clientId: req.body.client_id });
res.redirect(`${req.body.redirect_uri}?code=${authCode}`);
});
app.post('/token', (req, res) => {
const { code, client_id, client_secret } = req.body;
const accessToken = jwt.sign(
{ sub: authCode.userId, iss: 'my-server' },
process.env.JWT_SECRET,
{ expiresIn: '15m' }
);
res.json({
access_token: accessToken,
token_type: 'Bearer',
expires_in: 900,
refresh_token: '...'
});
});
六、OAuth 与 OpenID Connect
6.1 OIDC = OAuth + 身份认证
{
"iss": "https://provider.example",
"sub": "12345",
"aud": "client-id",
"exp": 123456,
"iat": 123450,
"name": "John Doe",
"email": "john@example.com"
}
七、刷新令牌
7.1 Refresh Token 流程
app.post('/refresh', (req, res) => {
const { refresh_token } = req.body;
const decoded = jwt.verify(refresh_token, process.env.REFRESH_SECRET);
if (tokenBlacklist.has(refresh_token)) {
return res.status(401).json({ error: 'Invalid token' });
}
const newAccessToken = jwt.sign(
{ sub: decoded.sub },
process.env.JWT_SECRET,
{ expiresIn: '15m' }
);
res.json({
access_token: newAccessToken,
token_type: 'Bearer',
expires_in: 900
});
});
八、安全清单