无感知刷新Token示例简析

2023-04-07编程技术94883

引言

在前后端分离的应用中,使用token进行认证是一种较为常见的方式。但是,由于token的有效期限制,需要不断刷新token,否则会导致用户认证失败。为了解决这个问题,可以实现无感知刷新token的功能,本文将介绍如何实现无感知刷新token。

token认证的原理

在web应用中,常见的token认证方式有基于cookie和基于token的认证。基于cookie的认证方式是将认证信息保存在cookie中,每次请求时将cookie发送给服务器进行认证;而基于token的认证方式是将认证信息保存在token中,每次请求时将token发送给服务器进行认证。

在基于token的认证方式中,客户端将认证信息保存在token中,而不是保存在cookie中。在认证成功后,服务器将生成一个access token和一个refresh token,并将它们返回给客户端。access token用于访问受保护的api,refresh token用于获取新的access token。

什么是无感知刷新token

无感知刷新token是指,在token过期之前,系统自动使用refresh token获取新的access token,从而实现token的无感知刷新,用户可以无缝继续使用应用。

在实现无感知刷新token的过程中,需要考虑以下几个方面:

  • 如何判断token是否过期?
  • 如何在token过期时自动使用refresh token获取新的access token?
  • 如何处理refresh token的安全问题?

下面将介绍如何实现无感知刷新token的具体步骤。

实现步骤

步骤一:获取access token和refresh token

在认证成功后,需要将access token和refresh token发送给客户端。access token用于访问受保护的api,refresh token用于获取新的access token。可以使用jwt(json web token)或oauth2(开放授权)等方式实现认证。

在jwt中,可以使用如下代码生成access token和refresh token:

const accesstoken = jwt.sign({userid: '123'}, 'access_token_secret', {expiresin: '15m'});
const refreshtoken = jwt.sign({userid: '123'}, 'refresh_token_secret', {expiresin: '7d'});

步骤二:在请求中携带access token

在每个需要认证的api请求中,需要在请求头中携带access token,如下所示:

get /api/user http/1.1
host: example.com
authorization: bearer eyjhbgcioijiuzi1niisinr5cci6ikpxvcj9...

在前端中,可以使用axios等库设置请求头:

axios.defaults.headers.common['authorization'] = `bearer ${accesstoken}`;

步骤三:拦截401 unauthorized响应

在服务器返回401 unauthorized响应时,说明access token已经过期,需要使用refresh token获取新的access token。可以使用axios拦截器或fetch api的中间件实现拦截。

在axios中,可以使用如下代码实现拦截器:

axios.interceptors.response.use(response => {
  return response;
}, error => {
  const originalrequest = error.config;
  if (error.response.status === 401 && !originalrequest._retry) {
    originalrequest._retry = true; //防止无限调用
    return axios.post('/api/refresh_token', {refreshtoken})
      .then(response => {
        const { access_token, refresh_token } = response.data;
        localstorage.setitem('access_token', access_token);
        localstorage.setitem('refresh_token', refresh_token);
        axios.defaults.headers.common['authorization'] = `bearer ${access_token}`;
        originalrequest.headers.authorization = `bearer ${access_token}`;
        return axios(originalrequest);
      });
  }
  return promise.reject(error);
});

在fetch中,可以使用如下代码实现中间件:

function authmiddleware(request) {
  const access_token = localstorage.getitem('access_token');
  if (access_token) {
    request.headers.set('authorization', `bearer ${access_token}`);
  }
  return request;
}
function tokenrefreshmiddleware(response) {
  if (response.status === 401) {
    const refreshtoken = localstorage.getitem('refresh_token');
    return fetch('/api/refresh_token', {
      method: 'post',
      headers: {
        'content-type': 'application/json'
      },
      body: json.stringify({ refreshtoken })
    }).then(response => {
      if (response.ok) {
        return response.json();
      }
      throw new error('refresh token failed');
    }).then(data => {
      localstorage.setitem('access_token', data.access_token);
      localstorage.setitem('refresh_token', data.refresh_token);
      return promise.resolve('refreshed');
    }).catch(error => {
      localstorage.removeitem('access_token');
      localstorage.removeitem('refresh_token');
      return promise.reject(error);
    });
  }
  return promise.resolve('ok');
}
fetch('/api/user', {
  method: 'get',
  headers: {
    'content-type': 'application/json'
  },
  middleware: [authmiddleware, tokenrefreshmiddleware]
}).then(response => {
  console.log(response);
}).catch(error => {
  console.error(error);
});

在上述代码中,使用axios或fetch拦截器拦截401 unauthorized响应,如果发现access token已经过期,则发送refresh token请求获取新的access token,并将新的access token设置到请求头中,重新发送请求。

步骤四:服务器处理refresh token请求

在服务器端,需要编写api处理refresh token请求,生成新的access token,并返回给客户端。

在jwt中,可以使用如下代码生成新的access token:

const accesstoken = jwt.sign({userid: '123'}, 'access_token_secret', {expiresin: '15m'});

在刷新token时,需要验证refresh token的合法性,可以使用如下代码验证refresh token:

try {
  const payload = jwt.verify(refreshtoken, 'refresh_token_secret');
  const accesstoken = jwt.sign({userid: payload.userid}, 'access_token_secret', {expiresin: '15m'});
  const refreshtoken = jwt.sign({userid: payload.userid}, 'refresh_token_secret', {expiresin: '7d'});
  res.json({access_token: accesstoken, refresh_token: refreshtoken});
} catch (err) {
  res.sendstatus(401);
}

在上述代码中,使用jwt的verify方法验证refresh token的合法性,如果验证成功,则生成新的access token和refresh token,并返回给客户端。

步骤五:设置定时刷新token

为了避免access token过期时间太长,可以设置定时刷新token的功能。可以使用定时器或web workers等方式实现定时刷新token。在每次刷新token时,需要重新获取新的access token和refresh token,并保存到客户端。

function refreshtoken() {
  const refreshtoken = localstorage.getitem('refresh_token');
  axios.post('/api/refresh_token', {refreshtoken})
    .then(response => {
      const { access_token, refresh_token } = response.data;
      localstorage.setitem('access_token', access_token);
      localstorage.setitem('refresh_token', refresh_token);
      axios.defaults.headers.common['authorization'] = `bearer ${access_token}`;
    })
    .catch(error => {
      console.error(error);
    });
}
setinterval(refreshtoken, 14 * 60 * 1000); // 每14分钟刷新token

在上述代码中,使用定时器每14分钟刷新token。在刷新token成功后,将新的access token和refresh token保存到客户端,并将新的access token设置到请求头中。

安全性考虑

在实现无感知刷新token的过程中,需要考虑到refresh token的安全性问题。因为refresh token具有长期的有效期限,一旦refresh token被泄露,攻击者就可以使用refresh token获取新的access token,从而绕过认证机制,访问受保护的api。

为了增加refresh token的安全性,可以考虑以下几种措施:

  • 将refresh token保存在httponly cookie中,可以避免在客户端被javascript获取;
  • 对refresh token进行加密或签名,可以增加其安全性。
  • 将refresh token保存在后端,前端通过接口和后端交互,实现刷新access token。

以上就是无感知刷新token的详细内容,更多关于无感知刷新token的资料请关注其它相关文章!

本文地址:https://www.ufcn.cn/tutorials/2424221.html

如非特殊说明,本站内容均来自于网友自主分享,概不代表本站观点,如有任何问题我们都将在收到反馈后的第一时间进行处理!