如何使用 JWT 刷新令牌生成新的访问令牌

Posted

技术标签:

【中文标题】如何使用 JWT 刷新令牌生成新的访问令牌【英文标题】:How to use a JWT Refresh Token to generate a new Access Token 【发布时间】:2019-03-08 03:31:52 【问题描述】:

我正在使用 Node.js 和 Express 来处理 JWT Auth。首先,每次创建和验证用户时,我都会在 User 集合中存储一个 refresh token

const refreshToken = await jwt.sign( userId: decoded.user , process.env.JWT_Refresh_Key);
const user = await User.updateOne( _id: mongoose.Types.ObjectId(decoded.user) ,  refresh_token: refreshToken, status: true );

成功登录后生成 JWT 访问令牌(15 分钟后过期):

const token = await jwt.sign(
                 email: user.email, userId: user._id, role: user.role ,
                process.env.JWT_Key,
                 expiresIn: '15m' );
res.status(200).json(success: true, token: token);

然后access token 存储在localStorage 中,由Angular Http Interceptor 和auth 方法处理。 15分钟后,token将无法处理请求,所以我需要使用数据库中存储的refresh token

正在AuthService.ts 上调用刷新方法:

export class AuthService 
  constructor(private http: HttpClient)

  refreshToken(token: string) 
      this.http.post<token: string, expiresIn: number, name: string>(`$BACKEND_URL/refreshtoken`,     token: token)
        .subscribe((data) => 
          const expiresAt = moment().add(data.expiresIn, 'second');
          localStorage.setItem('token', data.token);
          localStorage.setItem('expiration', JSON.stringify(expiresAt.valueOf()));
        );
    


//route
router.post('/refreshtoken', user.refreshToken);

//controller user.js
exports.refreshToken = async(req, res, next) => 
    // I need to store and call 'old_refreshtoken' where??
    const user = await User.findOne( refresh_token: old_refreshtoken );
    if (user) 
        const newToken = await jwt.sign(
             email: user.email, userId: user._id, role: user.role ,
            process.env.JWT_Key,
             expiresIn: '15m' );
        res.status(200).json(success: true, token: newToken, expiresIn: 900, name: user.name);
     else 
        res.status(401).json(success: false, message: 'Autenticação falhou.');
    
;

如何使用我的refresh token(数据库)生成新的access token? 我不确定如何在客户端 (Angular) 上存储 refresh token 以与存储在数据库中的 refresh token 进行比较。

【问题讨论】:

【参考方案1】:

我假设您有自己的后端来处理刷新 令牌过程。如果不是这种情况请告诉我

我对这个过程所做的是将所有解码和编码移到后端。但是您必须确保将最新的活动刷新令牌存储在后端。否则,有人可以重用旧令牌来创建访问令牌。

    在前端存储中的到期日期。然后,每次向后端发出请求时,检查是否未超过到期日期(可能您要考虑请求的延迟,例如到期前 5 秒)。如果已过期,请触发 refresh-token 方法。 在back-end 中创建一个刷新令牌端点并将access-tokenrefresh-token 发送给它 解码access-token 并获取您需要的数据。忽略此解码函数中的到期日期。 将refresh-token 与数据库中最新的refresh-token 进行比较。如果不匹配,则用户未被授权。否则,请继续。 现在,如果您想重用旧数据,则无需查询数据库,只需将access-token 内容重新编码为新令牌即可。否则,请执行查询并重建 access-token

希望对你有帮助

【讨论】:

【参考方案2】:

首先,您拥有刷新令牌方法的用户控制器不需要下一个参数,因为您没有使用它。 The next parameter work closely like a promise, It shines when it comes to middleware handler.

中间件流应该与控制器分离,取决于您的应用程序文件夹结构。

您可以按照与成功登录前相同的方式创建刷新令牌端点。

 refreshToken = (payload, secret) => 
  const token = jwt.sign(payload, secret, 
    expiresIn: '15m',
  );
  return token
 

然后使用 jwt 验证方法 jwt.verify(token, secret, handler) 验证令牌。

exports.refreshToken = (req, res, next) => 
 const token = req.headers['x-access-token'] || req.headers.token;
 /* if token was not provided */
 if (!token) 
    return res.status(403).send(
        success: false,
        message: 'Not Authorized',
    );
 
 /* verify token*/
 jwt.verify(token, secret, (error, decoded) => 
    if (error) 
        return res.status(401).send(
            success: false,
            message: 'Invalid token',
        );
    
    req.decoded = decoded;
    next();
 );

在前端存储到期日期(令牌)。然后,每次你向 后端,使用 jwt 验证方法检查令牌是否仍然有效(过期)。如果它已过期,请触发 refresh-token 方法。

您可以在 req 参数中获取解码后的令牌,例如 req.decoded。 使用localStoragelocalStorage.setItem(userToken, token)在客户端存储token,并通过localStorage.getItem(userToken)获取token。

【讨论】:

以上是关于如何使用 JWT 刷新令牌生成新的访问令牌的主要内容,如果未能解决你的问题,请参考以下文章

刷新令牌如何为 jwt 添加安全性?

黑名单/验证/生成 JWT 刷新令牌

如何在资源请求上重新生成刷新令牌和访问令牌?

使用 Angular 和 JWT 令牌持续登录

使用刷新令牌和访问令牌如何比仅使用 1 个 JWT 更“安全”?

使用 JWT 刷新令牌如何安全?