在小程序开发中,我们都知道小程序是没有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请求相关推荐

  1. Laravel使用Dingo API+JWT实现认证机制 无痛刷新Token

    Laravel使用Dingo API+JWT实现认证机制 无痛刷新Token 一.安装[Dingo API](https://github.com/dingo/api) 和 [JWT](https:/ ...

  2. axios 登录后设置header_axios如何利用promise无痛刷新token

    需求 最近遇到个需求:前端登录后,后端返回token和token有效时间,当token过期时要求用旧token去获取新的token,前端需要做到无痛刷新token,即请求刷新token时要做到用户无感 ...

  3. token过期后刷新token并重新发起请求

    系统登录成功后,后端返回token和refreshToken,所有请求都携带token,token如果过期接口将返回401,此时前端需要拿着refreshToken去刷新token,刷新后将拿到新的t ...

  4. uniapp全局请求插件luch-request 无痛刷新token

    场景:单独请求遇到后台接口请求返回401或者400时,需要重新刷新token再重新请求原本的接口,又不能迫使用户刷新页面. let http = new Request({baseURL: url, ...

  5. uniapp使用uview(luch-request),无痛刷新token

    // 此vm参数为页面的实例,可以通过它引用vuex中的变量 module.exports = (vm) => {// 初始化请求配置uni.$u.http.setConfig((config) ...

  6. 使用 Jwt-Auth 实现 API 用户认证以及无痛刷新访问令牌

    最近在做一个公司的项目,前端使用 Vue.js,后端使用 Laravel 构建 Api 服务,用户认证的包本来是想用 Laravel Passport 的,但是感觉有点麻烦,于是使用了 jwt-aut ...

  7. Laravel 5.5 使用 Jwt-Auth 实现 API 用户认证以及无痛刷新访问令牌

    最近在做一个公司的项目,前端使用 Vue.js,后端使用 Laravel 构建 Api 服务,用户认证的包本来是想用 Laravel Passport 的,但是感觉有点麻烦,于是使用了 jwt-aut ...

  8. 前端token知识梳理:token如何存储?token过期如何处理?如何无感刷新token?

    在前后端是以token的形式交互,既然是token,那么肯定有它的过期时间(为了接口数据的安全,服务器的token一般不会设置太长,根据需要一般是1-7天的样子),没有一个token是永久的,永久的t ...

  9. 前端刷新token,判断token是否过期,若没有过期则刷新token,过期则退出登录

    所用框架 vue+axios 为什么要刷新token 假设后端设置的token过期时间为10分钟.那么登录以后,过十分钟后token就会过期,这时再去操作系统,所有的请求都不能用,都会报token过期 ...

最新文章

  1. 重磅《美国机器智能国家战略》
  2. 一些实用的 jQuery 技巧
  3. 【原创视频教程】学生信息管理系统6--学员信息管理(完结篇)
  4. 内核驱动漏洞与攻击预防--by MJ0011
  5. 对PostgreSQL cmin和cmax的理解
  6. php7.0扩展yac,php扩展之yac安装
  7. BeyondCompared4提示“缺少评估信息或损坏” 一条命令解决
  8. ERP项目实施记录02
  9. HyperLeger Composer 重启 | 进入play ground | 进入 couchdb
  10. IO(一)----字节流
  11. 摄像头录像软件测试初学者,几款好用的摄像头视频捕获工具推荐
  12. 英文论文PDF全文翻译途径整理
  13. ROS之velodyne
  14. 第三方登录数据库用户表结构设计
  15. 大学慕课MOOC设计一个简单的计算工具
  16. 第五章:物理与环境安全技术
  17. mpu6050.py
  18. shell调用c程序,求子网掩码
  19. php-新特性之match解析
  20. mysql病历管理_医院肛肠科病历管理及诊断系统设计与实现(SSI,MySQL)

热门文章

  1. 扫描线填充算法(DDA应用)
  2. Python 第一个Hello World 实例
  3. 在火狐浏览器Firefox当中设置地址栏默认打开新标签页
  4. [28000][Microsoft][SQL server native client 10.0][SQL server]用户‘sa’登录失败。(18456)
  5. 生成式模型 生成对抗网络——资料梳理(专访资料 + 论文分类)
  6. SVN commit error:is part of the commit
  7. 调用天气插件如何让它不能点击跳转
  8. s()++php,.phps(dotphps)文件类型以及MIME类型详细描述
  9. 增强云服务器的安全的措施有哪些
  10. 用 Qt 控制 Nikon 显微镜的电动物镜转盘