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

时间:2018-10-02 23:57:48

标签: javascript node.js angular jwt

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

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 });

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

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? 我不确定如何在客户端(角度)上存储refresh token与存储在数据库中的refresh token进行比较。

2 个答案:

答案 0 :(得分:2)

  

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

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

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

希望这会有所帮助

答案 1 :(得分:0)

首先,具有刷新令牌方法的用户控制器不需要下一个参数,因为您没有使用它。 The next parameter work closely like a promiseIt 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 verify方法检查令牌是否仍然有效(过期)。如果已过期,请触发refresh-token方法。

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