基本上,当用户登录时,我将 access_token(jwt) 保存在存储在数据库中的刷新令牌对象中。请参阅下面保存的对象的示例;
const newToken = new RefreshToken({
issuedUtc: moment().unix(), /* Current unix date & time */
expiresUtc: moment().add(4, "days").unix(), /* Current unix date&time + 4 days */
token: refreshToken, /* Generate random token */
user: data.id, /* user id */
/* Signing the access Token */
access_token: jwt.sign(
{ sub: data.id, user: userWithoutHash },
Config.secret,
{
issuer: "http://localhost:3000",
expiresIn: "30m", // Expires in 30 minutes
}
),
});
然后将生成并保存的 rand 令牌作为 httpOnly cookie 发送到浏览器;
res.cookie("refreshToken", newToken.token, {
httpOnly: true,
sameSite: "strict",
});
由于浏览器为每个请求发送cookie,剩下的就是在受保护的路由上使用中间件,从cookie中检索令牌,通过在数据库中查找它来验证它是否存在,检查它是否没有过期,尝试验证保存在数据库中的访问令牌以获取该刷新令牌,如果已过期,则签署新的 jwt 并更新数据库中的刷新令牌,然后允许用户继续使用受保护的路由,如果有效,则允许用户继续前往受保护的路线。如果刷新令牌已过期,将用户重定向到登录页面,最后如果没有收到刷新令牌也将用户重定向到登录页面。
var cookie = await getcookie(req); // get the cookie as js object using my custom helper function
/* Check if refresh token was received */
if (cookie.refreshToken) {
/* Check find the refresh token object in the database */
var refreshToken = await RefreshToken.findOne({
token: cookie.refreshToken,
});
/* Check if the refresh token is still valid using expiry date */
if (moment.unix(refreshToken.expiresIn) > moment.now()) {
/* If the condition is fulfilled try to verify the access token using jwt */
jwt.verify(refreshToken.access_token, Config.secret, async (err, result) => {
/* in callback check for error */
if (err) {
/* If error this means the access_token is expired, so find and update the user's refresh token with a newly signed access token */
await RefreshToken.findByIdAndUpdate(refreshToken.id, {
access_token: jwt.sign(
{ sub: result.id, user: result.user },
Config.secret,
{
issuer: "http://localhost:3000",
expiresIn: "30m", // Expires in 30 minutes
}
),
});
/* Proceed to save the user in a local variable then call next */
res.locals.user = result.user;
return next();
}
/* If no error proceed by saving the user in a local variable then call next */
res.locals.user = result.user;
return next();
});
} else {
/* If the refresh token is expired, then redirect to log in */
return res.status(401).redirect('/login');
}
} else {
/* If no refresh token is provided, then redirect to log in */
return res.status(401).redirect('/login');
}
这是我自己想出来的,所以我不能说它是完整的证明,但是由于无法在 DOM 中访问 httpOnly cookie,因此在 DOM 中运行恶意脚本无法访问刷新令牌,即使刷新令牌不知何故落入坏人手中,那么它将毫无用处,因为它在到达服务器之前根本不保存任何信息。因此,只要在服务器上设置了正确的 cors 标头,就极不可能使用刷新令牌泄露任何信息。