引言
在前后端分离的应用中,使用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的资料请关注其它相关文章!