介绍
token:
- 作用:在访问一些接口时,需要传入token,就是它。
有效期:2小时(安全)。
refresh_token:
作用: 当token的有效期过了之后,可以使用它去请求一个特殊接口(这个接口也是后端指定的,明确需要传入refresh_token),并返回一个新的token回来(有效期还是2小时),以替换过期的那个token。
有效期:14天。(最理想的情况下,一次登陆可以持续14天。)
安全和体验
- 从安全角度来看 token 必须要具有一定的时效性,失效后的 token 不再能标识用是登录状态。
- 另外也要考虑用户的体验,例如用户在 token 失效的前 1 分钟打开小程序,用户浏览小程序 1 分钟后 token 失效,用户不得不再次去登录,这样的用户体验是极差的。
- 为了既能保证安全性又兼顾用户体验,咱们需要能够自动刷新 token 的方法,即 refresh_token。
refresh_token工作机制
- 用户在首次完成登录时会分别得到 token 和 refresh_token
- 当 token 失效后(例如2小时之后),调用接口A会返回 401 状态码(这是与后端约定好的规则)
- 检测状态码是否为 401,如果是,则携带refreshToken去调用刷新token的接口
- 刷新 token 的接口后会返回新的 token 和 refreshToken
- 把401的接口A重新发送一遍
注意:
refresh_token也是有过期时间的,只不过一般会比token过期时间更长一些。这就是为啥如果某个应用我们天天打开,则不会提示我们登录,如果是有几周或更长时间去打开时,会再次要求我们登录。
refresh_token一个更常见的名字叫token无感刷新。
refreshToken功能-基本实现
操作如下:
- 正常登录进入页面
- 在本地存储中直接修改localstorage的token值,模拟token失效。
- 刷新页面,重新发一次请求(此时请务必确保请求拦截器中的token是从本地存储中拿到的)
- 出现401错误。
主要代码
// 响应拦截器
http.intercept.response = async ({ statusCode, data, config }) => {
// debugger
console.log(statusCode, data, config)
// console.log(statusCode) // http 响应状态码
// console.log(config) // 发起请求时的参数
if (data.code === 401) {
const app = getApp()
// 调用接口获取新的 token
const res = await http({
url: '/refreshToken',
method: 'POST',
header: {
Authorization: 'Bearer ' + app.getToken('refreshToken'),
}
})
app.setToken('token', res.token)
app.setToken('refreshToken', res.refreshToken)
config = Object.assign(config, {
header: {
// 更新后的 token
Authorization: 'Bearer ' + res.token,
},
})
// 重新发请求
return http(config)
}
// 拦截器处理后的响应结果
if (data.code === 10000) {
return data.data
} else {
wx.$toast(data.message || '请求失败')
return Promise.reject(data.message)
}
}
当refreshToken也过期时做特殊处理
改进
代码
// 响应拦截器
http.intercept.response = async ({ statusCode, data, config }) => {
// debugger
console.log(statusCode, data, config)
// console.log(statusCode) // http 响应状态码
// console.log(config) // 发起请求时的参数
if (data.code === 401) {
if (config.url.includes('/refreshToken')) {
console.log('/refreshToken过期了')
// 获取当前页面的路径,保证登录成功后能跳回到原来页面
const pageStack = getCurrentPages()
const currentPage = pageStack.pop()
const redirectURL = currentPage.route
// 跳由跳转(登录页面)
wx.redirectTo({
url: '/pages/login/index?redirectURL=/' + redirectURL,
})
return Promise.reject('refreshToken也过期了,就只能重新登录了')
}
const app = getApp()
// 调用接口获取新的 token
const res = await http({
url: '/refreshToken',
method: 'POST',
header: {
Authorization: 'Bearer ' + app.getToken('refreshToken'),
}
})
app.setToken('token', res.token)
app.setToken('refreshToken', res.refreshToken)
config = Object.assign(config, {
header: {
// 更新后的 token
Authorization: 'Bearer ' + res.token,
},
})
// 重新发请求
return http(config)
}
// 拦截器处理后的响应结果
else if (data.code === 10000) {
return data.data
} else {
wx.$toast(data.message || '请求失败')
return Promise.reject(data.message)
}
}
大功告成!