无痛刷新token续接401请求
在小程序开发中,我们都知道小程序是没有cookie的,那么用户身份是如何确定的,后段颁发token,前端每次请求头部附带token。
既然是token,那么肯定有它的过期时间,没有一个token是永久的,永久的token就相当于一串永久的密码,是不安全的,
那么既然有刷新时间,问题就来了
1.前后端交互的过程中token如何存储?
2.token过期时,前端该怎么处理?
3.当用户正在操作时,遇到token过期该怎么办?直接跳回登陆页面?
token如何存储?
cookie的大小约4k,兼容性在ie6及以上 都兼容,在浏览器和服务器间来回传递,因此它得在服务器的环境下运行,而且可以设定过期时间,默认的过期时间是session会话结束。
localStorage的大小约5M,兼容性在ie7及以上都兼容,有浏览器就可以,不需要在服务器的环境下运行, 会一直存在,除非手动清除 。
答案大致分为2种
存在 cookie
中
存在 localStorage
中
token过期时,前端该怎么处理?
1.第一种:跳回登陆页面重新登陆
2.第二种:拦截401重新获取token
class HttpClient {/*** Create a new instance of HttpClient.*/constructor() {this.interceptors = {request: [],response: []};}/*** Sends a single request to server.** @param {Object} options - Coming soon.*/sendRequest(options) {let requestOptions = options;if (!requestOptions.header) {requestOptions.header = {};}// 重新设置 Accept 和 Content-TyperequestOptions.header = Object.assign({Accept: 'application/json, text/plain, */*','Content-Type': 'application/json;charset=utf-8'},requestOptions.header);this.interceptors.request.forEach((interceptor) => {const request = interceptor(requestOptions);requestOptions = request.options;});// 将以 Promise 返回数据, 无 success、fail、complete 参数// let response = uni.request(requestOptions);// 使用Promise包装一下, 以 complete方式来接收接口调用结果let response = new Promise((resolve, reject) => {requestOptions.complete = (res) => {const { statusCode } = res;const isSuccess = (statusCode >= 200 && statusCode < 300) || statusCode === 304;if(statusCode==401){ //拦截401请求uni.reLaunch({ //关闭所有页面直接跳到登陆页面url: '/pages/login/login'});}if (isSuccess) {if(res.data.code==1){resolve(res.data);}else{reject(res);}} else {reject(res);}};requestOptions.requestId = new Date().getTime();uni.request(requestOptions);});this.interceptors.response.forEach((interceptor) => {response = interceptor(response);});return response;}
}export default HttpClient;
这种方法适用余有登陆页面的小程序,但同样存在问题,假如用户在填写表单,填写完毕你却告诉我重新登陆,确定用户不会卸掉你的APP???
有人说了 异常退出 我会本地缓存填写的表单内容,当然你要是能接受这种我也无话可说!!!
我们要做的是无痛刷新toekn,那么首先要在401拦截的时候去重新登陆获取新的token
继续优化改造
import store from "../store/index.js";
class HttpClient {/*** Create a new instance of HttpClient.*/constructor() {this.interceptors = {request: [],response: []};}/*** Sends a single request to server.** @param {Object} options - Coming soon.*/sendRequest(options) {let requestOptions = options;if (!requestOptions.header) {requestOptions.header = {};}// 重新设置 Accept 和 Content-TyperequestOptions.header = Object.assign({Accept: 'application/json, text/plain, */*','Content-Type': 'application/json;charset=utf-8'},requestOptions.header);this.interceptors.request.forEach((interceptor) => {const request = interceptor(requestOptions);requestOptions = request.options;});// 将以 Promise 返回数据, 无 success、fail、complete 参数// let response = uni.request(requestOptions);// 使用Promise包装一下, 以 complete方式来接收接口调用结果let response = new Promise((resolve, reject) => {requestOptions.complete = (res) => {const { statusCode } = res;const isSuccess = (statusCode >= 200 && statusCode < 300) || statusCode === 304;if(statusCode==401){ //拦截401请求store.dispatch("auth/login") //调用vueX中的登陆将登陆信息保存到VueX.then(()=>{ //提示用户刚才操作无效重新操作一次uni.showToast({title: '请重新操作',duration: 2000,icon: "none",});}).catch(()=>{uni.showToast({title: '账户异常请重启程序',duration: 2000,icon: "none",}); })}if (isSuccess) {if(res.data.code==1){resolve(res.data);}else{reject(res);}} else {reject(res);}};requestOptions.requestId = new Date().getTime();uni.request(requestOptions);});this.interceptors.response.forEach((interceptor) => {response = interceptor(response);});return response;}
}export default HttpClient;
到此我们实现的在401错误时候去重新登陆获取新的token,且告知用户重新操作一次
到此你会发现一个问题,当页面存在一个请求,目前方案毫无问题,但是当存在两个、三个、四个请求,你会骂娘
失败3个请求会重新调用3次登陆会刷新3次token
那么此时要做的就是保证不多次登陆
思路加一个开关,当在登陆过程中后续错误不再走登陆接口
import store from "../store/index.js";// 是否正在重新登陆刷新的标记
var loginRefreshing = falseclass HttpClient {/*** Create a new instance of HttpClient.*/constructor() {this.interceptors = {request: [],response: []};}/*** Sends a single request to server.** @param {Object} options - Coming soon.*/sendRequest(options) {let requestOptions = options;if (!requestOptions.header) {requestOptions.header = {};}// 重新设置 Accept 和 Content-TyperequestOptions.header = Object.assign({Accept: 'application/json, text/plain, */*','Content-Type': 'application/json;charset=utf-8'},requestOptions.header);this.interceptors.request.forEach((interceptor) => {const request = interceptor(requestOptions);requestOptions = request.options;});// 将以 Promise 返回数据, 无 success、fail、complete 参数// let response = uni.request(requestOptions);// 使用Promise包装一下, 以 complete方式来接收接口调用结果let response = new Promise((resolve, reject) => {requestOptions.complete = (res) => {const { statusCode } = res;const isSuccess = (statusCode >= 200 && statusCode < 300) || statusCode === 304;if(statusCode==401){ //拦截401请求if(!loginRefreshing){//防止重复登陆loginRefreshing = truestore.dispatch("auth/login") //调用vueX中的登陆将登陆信息保存到VueX.then(()=>{ //提示用户刚才操作无效重新操作一次uni.showToast({title: '请重新操作',duration: 2000,icon: "none",});}).catch(()=>{uni.showToast({title: '账户异常请重启程序',duration: 2000,icon: "none",}); }).finally(()=>{//销毁 是否正在重新登陆刷新的标记loginRefreshing = false});}}if (isSuccess) {if(res.data.code==1){resolve(res.data);}else{reject(res);}} else {reject(res);}};requestOptions.requestId = new Date().getTime();uni.request(requestOptions);});this.interceptors.response.forEach((interceptor) => {response = interceptor(response);});return response;}
}export default HttpClient;
我们可以看到在遇到两个401错误时候并没有请求两次login,只请求一次,到此刷新token算是完成了,但是需要用户配合去重新操作一次,还不是真正的无痛刷线token,做到用户无感知
思路:将请求401的请求缓存起来,在重新登陆完成之后再将缓存中的请求重新发出,
废话不多说直接上代码
import AuthService from "@/services/auth.service";
import store from "../store/index.js";
// 是否正在重新登陆刷新的标记
var loginRefreshing = false
// 重试队列,每一项将是一个待执行的函数形式
let requests = []class HttpClient {/*** Create a new instance of HttpClient.*/constructor() {this.interceptors = {request: [],response: []};}/*** Sends a single request to server.** @param {Object} options - Coming soon.*/sendRequest(options) {let requestOptions = options;if (!requestOptions.header) {requestOptions.header = {};}// 重新设置 Accept 和 Content-TyperequestOptions.header = Object.assign({Accept: 'application/json, text/plain, */*','Content-Type': 'application/json;charset=utf-8'},requestOptions.header);this.interceptors.request.forEach((interceptor) => {const request = interceptor(requestOptions);requestOptions = request.options;});// 将以 Promise 返回数据, 无 success、fail、complete 参数// let response = uni.request(requestOptions);// 使用Promise包装一下, 以 complete方式来接收接口调用结果let response = new Promise((resolve, reject) => {let timeId = setTimeout(()=>{reject({statusCode:504});},10000)requestOptions.complete = (res) => {clearTimeout(timeId)const { statusCode } = res;const isSuccess = (statusCode >= 200 && statusCode < 300) || statusCode === 304;if(statusCode==401){ //无痛刷新tokenif(!loginRefreshing){//防止重复登陆loginRefreshing = truestore.dispatch("auth/logout");store.dispatch("auth/login").then(()=>{//所有存储到对列组中的请求重新执行。requests.forEach(callback=>{callback(AuthService.getToken() ? AuthService.getToken() : "")})//重试队列清空requests = []}).catch(()=>{uni.showToast({title: '账户异常请重启程序',duration: 2000,icon: "none",}); }).finally(()=>{//销毁 是否正在重新登陆刷新的标记loginRefreshing = false});}return new Promise((resolve) => {// 将resolve放进队列,用一个函数形式来保存,等token刷新后直接执行requests.push((token) => {requestOptions.header.token = token //带着登陆后的新tokenresolve(uni.request(requestOptions))})})}if (isSuccess) {if(res.data.code==1){resolve(res.data);}else{reject(res);}} else {reject(res);}};requestOptions.requestId = new Date().getTime();uni.request(requestOptions);});this.interceptors.response.forEach((interceptor) => {response = interceptor(response);});return response;}
}export default HttpClient;
知识点,在重新获取新的token后要将缓存起来的请求中的token替换为重新登陆后新的token
到此无痛刷新token续接401请求的方法已经处理完毕,在用户提交表单时候遇到token失效重新获取新的token再续接表单请求,此时用户毫无感知,可能在请求时间上多了延迟,体验好感度+99,哈哈哈哈哈
到此无痛刷新token续接401已经完成请求快去试试吧
提示:有些后段在接口请求做了签名,记得像更换token一样在重新登陆完成之后更换新的时间戳新的签名等字段
github地址 https://github.com/Lemon-whales/base-mall
无痛刷新token续接401请求相关推荐
- Laravel使用Dingo API+JWT实现认证机制 无痛刷新Token
Laravel使用Dingo API+JWT实现认证机制 无痛刷新Token 一.安装[Dingo API](https://github.com/dingo/api) 和 [JWT](https:/ ...
- axios 登录后设置header_axios如何利用promise无痛刷新token
需求 最近遇到个需求:前端登录后,后端返回token和token有效时间,当token过期时要求用旧token去获取新的token,前端需要做到无痛刷新token,即请求刷新token时要做到用户无感 ...
- token过期后刷新token并重新发起请求
系统登录成功后,后端返回token和refreshToken,所有请求都携带token,token如果过期接口将返回401,此时前端需要拿着refreshToken去刷新token,刷新后将拿到新的t ...
- uniapp全局请求插件luch-request 无痛刷新token
场景:单独请求遇到后台接口请求返回401或者400时,需要重新刷新token再重新请求原本的接口,又不能迫使用户刷新页面. let http = new Request({baseURL: url, ...
- uniapp使用uview(luch-request),无痛刷新token
// 此vm参数为页面的实例,可以通过它引用vuex中的变量 module.exports = (vm) => {// 初始化请求配置uni.$u.http.setConfig((config) ...
- 使用 Jwt-Auth 实现 API 用户认证以及无痛刷新访问令牌
最近在做一个公司的项目,前端使用 Vue.js,后端使用 Laravel 构建 Api 服务,用户认证的包本来是想用 Laravel Passport 的,但是感觉有点麻烦,于是使用了 jwt-aut ...
- Laravel 5.5 使用 Jwt-Auth 实现 API 用户认证以及无痛刷新访问令牌
最近在做一个公司的项目,前端使用 Vue.js,后端使用 Laravel 构建 Api 服务,用户认证的包本来是想用 Laravel Passport 的,但是感觉有点麻烦,于是使用了 jwt-aut ...
- 前端token知识梳理:token如何存储?token过期如何处理?如何无感刷新token?
在前后端是以token的形式交互,既然是token,那么肯定有它的过期时间(为了接口数据的安全,服务器的token一般不会设置太长,根据需要一般是1-7天的样子),没有一个token是永久的,永久的t ...
- 前端刷新token,判断token是否过期,若没有过期则刷新token,过期则退出登录
所用框架 vue+axios 为什么要刷新token 假设后端设置的token过期时间为10分钟.那么登录以后,过十分钟后token就会过期,这时再去操作系统,所有的请求都不能用,都会报token过期 ...
最新文章
- 重磅《美国机器智能国家战略》
- 一些实用的 jQuery 技巧
- 【原创视频教程】学生信息管理系统6--学员信息管理(完结篇)
- 内核驱动漏洞与攻击预防--by MJ0011
- 对PostgreSQL cmin和cmax的理解
- php7.0扩展yac,php扩展之yac安装
- BeyondCompared4提示“缺少评估信息或损坏” 一条命令解决
- ERP项目实施记录02
- HyperLeger Composer 重启 | 进入play ground | 进入 couchdb
- IO(一)----字节流
- 摄像头录像软件测试初学者,几款好用的摄像头视频捕获工具推荐
- 英文论文PDF全文翻译途径整理
- ROS之velodyne
- 第三方登录数据库用户表结构设计
- 大学慕课MOOC设计一个简单的计算工具
- 第五章:物理与环境安全技术
- mpu6050.py
- shell调用c程序,求子网掩码
- php-新特性之match解析
- mysql病历管理_医院肛肠科病历管理及诊断系统设计与实现(SSI,MySQL)
热门文章
- 扫描线填充算法(DDA应用)
- Python 第一个Hello World 实例
- 在火狐浏览器Firefox当中设置地址栏默认打开新标签页
- [28000][Microsoft][SQL server native client 10.0][SQL server]用户‘sa’登录失败。(18456)
- 生成式模型 生成对抗网络——资料梳理(专访资料 + 论文分类)
- SVN commit error:is part of the commit
- 调用天气插件如何让它不能点击跳转
- s()++php,.phps(dotphps)文件类型以及MIME类型详细描述
- 增强云服务器的安全的措施有哪些
- 用 Qt 控制 Nikon 显微镜的电动物镜转盘