为什么需要双token?
token
是为了防止用户信息传来传去导致被劫持,但是假如token
没有过期时间或者过期很长,那么显然token
被劫持还是不安全的,token
就失去了意义。如果token时间短的话,安全性相对提高,但是需要频繁登录,频繁需要服务器返回,对服务器性能是一种浪费,为了解决安全性问题和用户频繁登录问题,采用双token来
设计。
双token的介绍和优点
介绍
双token机制是为了提高系统安全性而采用的一种措施。它指的是在登录成功后,会生成两个token返回给客户端:
- A***ess Token:用于访问受限资源,有一定的生命周期,过期需要重新获取。
- Refresh Token:用于刷新A***ess Token,生命周期较长
优点
1. 短期A***ess Token提高安全性,减小被盗用的时间窗口
2. 长期Refresh Token方便免登陆续期,优化用户体验
3. 两级验证机制,A***ess Token遭破解不会立即导致全部资源被盗用
具体流程
1. 客户端使用用户名密码请求登录
2. 服务端验证成功后,生成A***ess Token和Refresh Token返回给客户端
3. 客户端在A***ess Token过期前使用它访问受限资源
4. A***ess Token过期后,客户端使用Refresh Token请求新的A***ess Token
5. 服务端验证Refresh Token成功后,生成新的A***ess Token返回
6. 客户端使用新的A***ess Token继续访问资源
7. Refresh Token过期时,需要客户端重新登录获取新的A***ess Token和Refresh Token
实现步骤
1. 生成A***ess Token和Refresh Token,存储Refresh Token
2. 设置不同的过期时间,如A***ess Token 30分钟,Refresh Token 7天
3. 在A***ess Token过期时验证Refresh Token是否过期以及合法性
4. 如果通过验证,根据Refresh Token生成新的A***ess Token返回
5. 如果Refresh Token也过期,返回错误码提示重新登录
代码:
javascript">const jwt = require('jsonwebtoken');
const secretKey = 'secret'; // a***ess token密钥
const refreshSecret = 'refreshSecret'; // refresh token密钥
function getA***essToken(req, res) {
let { username, password } = req.body; // 获取登录表单数据
if (username && password) { // 校验用户名和密码
let a***essToken = jwt.sign({ username }, secretKey, { expiresIn: '30m' });
// 生成30分钟过期a***ess token
let refreshToken = jwt.sign({ username }, refreshSecret);
// 生成无过期refresh token
res.json({ // 返回a***ess token和refresh token
status: 'ok',
a***essToken,
refreshToken
})
} else {
res.json({
status: 'error',
message: '用户名或密码不能为空!' // 登录表单校验失败,返回错误信息
})
}
}
function refreshToken(req, res) { // refresh接口
let { refreshToken } = req.body; // 获取refresh token
if (refreshToken) { // 校验refresh token
let decoded = jwt.verify(refreshToken, refreshSecret);
// 验证refresh token
let a***essToken = jwt.sign({ username: decoded.username }, secretKey, { expiresIn: '30m' });
// 生成新的30分钟a***ess token
res.json({
status: 'ok',
a***essToken // 返回新的a***ess token
})
} else {
res.json({
status: 'error',
message: '刷新令牌不能为空!' // refresh token校验失败,返回错误信息
})
}
}
app.post('/login', getA***essToken); // 登录接口,获取token
app.post('/refresh', refreshToken); // 刷新接口,刷新a***ess token