现代Web安全实践:基于Token与Refresh Token的单点登录(SSO)实现

原创
06/20 10:46
阅读数 1.2K

在数字化转型加速的今天,单点登录(SSO)已成为企业身份管理的核心基础设施。袋鼠云UED团队在过去几年中,为金融、政务、医疗等领域的大型系统构建SSO解决方案。本文将分享基于Token的标准SSO实现,同时解密UED团队对于单点登录的安全实践方式。

单点登录核心原理

1、SSO的基本工作流程

2、双Token机制的优势

* **Access Token:短期有效(通常1-2小时),减少泄露风险**

* **Refresh Token:长期有效(通常7-30天),存储于HttpOnly Cookie中**

* 安全平衡:即使Access Token泄露,攻击者也难以获取新的有效Token

Node.js + JWT实现示例

1、认证服务核心代码

const jwt = require('jsonwebtoken');const crypto = require('crypto');

// 生成密钥对(实际项目应该预生成并妥善保存)const { privateKey, publicKey } = crypto.generateKeyPairSync('rsa', { modulusLength: 2048,});

// 生成Tokenfunction generateTokens(user) { const accessToken = jwt.sign( { userId: user.id, role: user.role }, privateKey, { algorithm: 'RS256', expiresIn: '1h' } );

const refreshToken = jwt.sign( { userId: user.id, tokenVersion: user.tokenVersion }, privateKey, { algorithm: 'RS256', expiresIn: '7d' } );

return { accessToken, refreshToken };}

// 验证中间件function authenticate(req, res, next) { const authHeader = req.headers['authorization']; const token = authHeader && authHeader.split(' ')[1];

if (!token) return res.sendStatus(401);

jwt.verify(token, publicKey, (err, user) => { if (err) return res.sendStatus(403); req.user = user; next(); });}

2、Token刷新接口设计

app.post('/refresh-token', (req, res) => { const refreshToken = req.cookies.refreshToken;

if (!refreshToken) return res.sendStatus(401);

jwt.verify(refreshToken, publicKey, async (err, payload) => { if (err) return res.sendStatus(403);

// 检查数据库中的token版本(防止被盗用) const user = await User.findById(payload.userId); if (user.tokenVersion !== payload.tokenVersion) { return res.sendStatus(403); }

// 颁发新Token const newTokens = generateTokens(user);

// 设置HttpOnly Cookie res.cookie('refreshToken', newTokens.refreshToken, { httpOnly: true, secure: true, sameSite: 'strict', maxAge: 7 * 24 * 60 * 60 * 1000 });

res.json({ accessToken: newTokens.accessToken }); });});

前端实现关键点

1、Axios拦截器配置

const api = axios.create({ baseURL: process.env.API_URL,});

// 请求拦截器api.interceptors.request.use(config => { const token = localStorage.getItem('accessToken'); if (token) { config.headers.Authorization = `Bearer ${token}`; } return config;});

// 响应拦截器api.interceptors.response.use( response => response, async error => { const originalRequest = error.config;

// 401错误且不是刷新请求 if (error.response.status === 401 && !originalRequest._retry) { originalRequest._retry = true;

try { const { data } = await axios.post('/refresh-token', {}, { withCredentials: true // 携带cookie });

localStorage.setItem('accessToken', data.accessToken); originalRequest.headers.Authorization = `Bearer ${data.accessToken}`; return api(originalRequest); } catch (refreshError) { // 刷新失败跳转登录 window.location = '/login'; return Promise.reject(refreshError); } }

return Promise.reject(error); });

2、安全存储方案对比

安全增强措施

1、Token撤销策略

2、多因素认证集成

app.post('/login', async (req, res) => { const { email, password } = req.body;

const user = await User.findOne({ email }); if (!user || !await bcrypt.compare(password, user.password)) { return res.status(401).send('认证失败'); }

// 检查是否开启MFA if (user.mfaEnabled) { const tempToken = jwt.sign( { userId: user.id, mfa: true }, privateKey, { expiresIn: '5m' } ); return res.json({ mfaRequired: true, tempToken }); }

// 常规登录流程 const tokens = generateTokens(user); sendTokens(res, tokens);});

app.post('/verify-mfa', authenticateTempToken, (req, res) => { const { code } = req.body;

if (verifyMfaCode(req.user.userId, code)) { const user = await User.findById(req.user.userId); const tokens = generateTokens(user); sendTokens(res, tokens); } else { res.status(401).send('MFA验证失败'); }});

最佳实践建议

`1、传输安全`

* 始终使用HTTPS

* 设置`Secure`和`SameSite`Cookie属性

2、防御措施

* 实现CSRF Token机制

* 设置速率限制(如1分钟5次刷新)

3、监控审计

4、失效处理

* 登出时清除客户端Token

* 服务端维护短期黑名单(JWT本身无状态)

Token与Refresh Token机制为现代SSO提供了优雅的解决方案。作为袋鼠云技术团队,我们在多个项目中验证了这一架构的可靠性,Token+RefreshToken方案在安全性和用户体验之间取得了良好平衡。实际部署时还需要考虑:

* 分布式环境下的密钥管理

* 多平台Token同步

* 会话超时策略

建议结合OAuth 2.0或OpenID Connect等标准协议实现生产级SSO系统。

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
0 评论
0 收藏
0
分享
返回顶部
顶部