flyio 无感刷新token

https://www.jianshu.com/p/b37a86fe0ac7?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

npm install flyio
var Fly=require('flyio/dist/npm/wx');
var fly=new Fly();
<!DOCTYPE html>
<html>
<head lang="zh-cmn-Hans">
    <meta charset="UTF-8">
    <title>Fly.js Demo</title>
    <meta name="renderer" content="webkit">
    <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
    <meta name="viewport" content="width=device-width,initial-scale=0.5,user-scalable=no"/>
    <!--[if lt IE 9]>
    <script src="//cdn.bootcss.com/es5-shim/4.5.9/es5-shim.min.js"></script>
    <script src="//cdn.bootcss.com/es5-shim/4.5.9/es5-sham.min.js"></script>
    <script src="//cdn.bootcss.com/json3/3.3.2/json3.min.js"></script>
    <![endif]-->
    <!--[if lt IE 10]>
    <script src="//cdn.bootcss.com/jquery-placeholder/2.3.1/jquery.placeholder.min.js"></script>
    <![endif]-->
    <!--<script src="https://cdn.bootcss.com/axios/0.16.2/axios.js"></script>-->
    <script src="../dist/fly.js"></script>

</head>
<style>
    html {
        font-size: 20px;
        -webkit-user-select: none;
    }
</style>
<body>
<div style="background: teal; font-size: 38px; color: #ffef68; text-shadow: 2px 2px 5px #ffef68; width: 400px; height: 400px; text-align: center">
    <div style="padding-top: 100px"> Fly.js</div>
</div>
open console panel to view log.
<script>

    //import fly from "../index";

    var csrfToken = "";
    //定义公共headers
    fly.config.headers = {"x-tag": "flyio"}
    fly.config.baseURL = "http://www.dtworkroom.com/doris/1/2.0.0/"
    var newFly = new Fly;
    newFly.config = fly.config;
    var log = console.log
    fly.interceptors.request.use(function (request) {
        log(`发起请求:path:${request.url},baseURL:${request.baseURL}`)
        if (!csrfToken) {
            log("没有token,先请求token...");
            //锁定当天实例,后续请求会在拦截器外排队
            fly.lock();
            return newFly.get("/token").then((d) => {
                request.headers["csrfToken"] = csrfToken = d.data.data.token;
                log("token请求成功,值为: " + d.data.data.token);
                log(`继续完成请求:path:${request.url},baseURL:${request.baseURL}`)
                return request
            }).finally(() => fly.unlock()) //解锁后,会继续发起请求队列中的任务
        } else {
            request.headers["csrfToken"] = csrfToken;
        }
    })

    //  response interceptors
    fly.interceptors.response.use(
        function (response) {
            log("interceptors.response", response)
            //验证失效
            if (response.data.data.tokenExpired) {
                log("token失效,重新请求token...");
                this.lock(); //锁定响应拦截器
                return newFly.get("/token")
                    .then((d) => {
                        csrfToken = d.data.data.token;
                        log("token已更新,值为: " + csrfToken);
                    })
                    .finally(() => this.unlock())
                    .then(() => {
                        log(`重新请求:path:${response.request.url},baseURL:${response.request.baseURL}`)
                        return fly.request(response.request);
                    })
            } else {
                return response.data.data;
            }
        },
        function (err) {
            log("error-interceptor", err)
        }
    )

    fly.get("/test?tag=1")
        .then(function (d) {
            log("请求成功:", d)
        }).catch(function (e) {
        log("请求失败", e)
    })
    fly.get("/test?tag=2")
        .then(function (d) {
            log("请求成功:", d)
        }).catch(function (e) {
        log("请求失败", e)
    })
    fly.get("/test?tag=3")
        .then(function (d) {
            log("请求成功:", d)
        }).catch(function (e) {
        log("请求失败", e)
    })

</script>
</body>
</html>

new Promise()

import store from '@/store'
import config from './config.js'
import Base64 from './base64.js'
import {
	refreshToken
} from '@/api/user'
// H5版本
// #ifdef H5
import Fly from "flyio/dist/npm/fly"
// #endif
//微信小程序和APP版本
// #ifndef H5
import Fly from "flyio/dist/npm/wx"
// #endif
const request = new Fly();
let base64 = new Base64();
request.interceptors.request.use(function(request) {
	request.baseURL = config.baseURL;
	const token = store.state.user.token;
	request.headers['Authorization'] = `Basic ${base64.encode(`${config.clientId}:${config.clientSecret}`)}`;
	if (token) {
		request.headers['Blade-Auth'] = 'bearer ' + token
	} else {
		request.headers['Tenant-Id'] = config.tenantId
	}
	if (request.method === 'POST' && request.headers['Content-Type'] !== 'multipart/form-data') {
		uni.showLoading({
			title: '',
			mask: true
		});
		setTimeout(()=>{
			uni.hideLoading()
		},800)
		request.body = {
			...request.body,
			_t: Date.parse(new Date()) / 1000
		}
	} else if (request.method === 'GET') {
		request.params = {
			_t: Date.parse(new Date()) / 1000,
			...request.params
		}
	}
	return request
})
request.interceptors.response.use(function(response) {
	console.log(response,"responense")
	// uni.hideLoading();
	return response.data
}, function(error) {
	console.log(error, "***********************************")
	// uni.hideLoading();
	let pages = getCurrentPages();
	console.log(pages)
	let route = pages[pages.length-1].route;
	if(route.indexOf('registerLogin') != -1) return;
	console.log(pages)
	if (error.response.data.code == 401) {
		// uni.hideLoading();
		this.lock();
		let _this = this;
		setTimeout(()=>{
			_this.unlock();
		},0)
		return store.dispatch('refreshToken')
			.then(res => {
				console.log(res,"SSSSSSSSSSS")
				_this.unlock()
				return request.request(error.request);
			})
			.finally(() => _this.unlock())
			.then(() => {
				console.log(`重新请求:path:${error.request.url},baseURL:${error.response.baseURL}`)
				return request.request(error.request);
			})
		
		// let request = error.request;
		// let headers = {}
		// headers['Authorization'] =
		// 	`Basic ${base64.encode(`${config.clientId}:${config.clientSecret}`)}`;
		// const token = store.state.user.token;
		// if (token) {
		// 	headers['Blade-Auth'] = 'bearer ' + token
		// } else {
		// 	headers['Tenant-Id'] = config.tenantId
		// }
		// let data = {...request.body}
		// refreshToken(store.state.user.refreshToken, store.state.user.tenantId).then(res => {
		// 	console.log(res, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
		// 	if (res.access_token) {
		// 		store.commit('SET_TOKEN', res.access_token)
		// 		headers['Blade-Auth'] = 'bearer ' + res.access_token
		// 		uni.showLoading({
		// 			mask: true
		// 		})
		// 		uni.request({
		// 			url: request.baseURL + request.url,
		// 			header: headers,
		// 			method: request.method,
		// 			data,
		// 			success: res => {
		// 				console.log(res, "真他吗成了")
		// 				console.log(store.state.user.token, "0000000000")
		// 				uni.hideLoading();
		// 				if (request.method === 'GET') {

		// 				} else {
		// 					uni.navigateBack({
		// 						delta: 1
		// 					})
		// 				}
		// 			},
		// 			fail: err => {
		// 				console.log("失败了")
		// 			}
		// 		})
		// 	} else {
		// 		uni.reLaunch({
		// 			url: '/pages/public/registerLogin/registerLogin'
		// 		})
		// 		setTimeout(() => {
		// 			uni.showModal({
		// 				content: '当前登录已过期,请重新登录',
		// 				showCancel: false,
		// 				confirmColor: '#FF6600',
		// 				success: (res) => {
		// 					if (res.confirm) {
		// 						console.log('用户点击确定');
		// 					}
		// 				}
		// 			});
		// 		}, 200)
		// 	}
		// })
	}
})

export default request
export {
	request
}

### 无感刷新 Token实现机制 无感刷新 Token 是一种技术手段,用于在用户不知情的情况下自动更新已过期的访问令牌(Access Token),从而避免因令牌失效而导致的操作中断。以下是其实现的核心原理: #### 核心原理 1. **双令牌体系**:通常采用 Access Token 和 Refresh Token 结合的方式。Access Token 负责短期认证,Refresh Token 则负责长期授权并用来获取新的 Access Token。 2. **拦截器模式**:利用 HTTP 请求库(如 Axios)中的请求/响应拦截器,在检测到 Access Token 过期时触发 Refresh Token 流程。 3. **全局状态管理**:为了防止多个并发请求同时尝试刷新 Token,需引入锁机制或其他同步策略。 --- ### Vue 2 中基于 Axios 的无感刷新 Token 实现代码示例 以下是一个完整的代码示例,展示如何在 Vue 2 项目中实现无感刷新 Token 功能: ```javascript // 定义一个标志位,表示当前是否有正在进行的 Token 刷新流程 let isRefreshing = false; let failedQueue = []; // 存储失败的请求队列 function refreshTokens() { const config = { headers: { Authorization: `Bearer ${localStorage.getItem('refresh_token')}` } }; return axios.post('/auth/token', {}, config).then(response => { localStorage.setItem('access_token', response.data.access_token); localStorage.setItem('refresh_token', response.data.refresh_token); // 对之前被拒绝的请求重新发送 failedQueue.forEach(req => req.onSuccess()); failedQueue = []; return Promise.resolve(); }).catch(error => { console.error("Failed to refresh tokens", error); failedQueue.forEach(req => req.onFailure?.(error)); failedQueue = []; return Promise.reject(error); }); } axios.interceptors.request.use(config => { const accessToken = localStorage.getItem('access_token'); if (accessToken) { config.headers.Authorization = `Bearer ${accessToken}`; } return config; }, error => { return Promise.reject(error); }); axios.interceptors.response.use(response => { return response; }, async function (error) { const originalRequest = error.config; if (error.response.status === 401 && !originalRequest._retry) { originalRequest._retry = true; // 防止无限循环 try { if (!isRefreshing) { isRefreshing = true; await refreshTokens(); // 执行 Token 刷新逻辑 isRefreshing = false; } return axios(originalRequest); // 使用新 Token 重发原请求 } catch (err) { if (failedQueue.length > 0) { failedQueue.push({ onSuccess: () => axios(originalRequest), onFailure: err => console.log(err.message) }); } throw new Error("Unable to refresh token"); } } return Promise.reject(error); }); ``` --- ### 关键点解析 1. **Token 存储位置**: 上述代码假设 Tokens 已存储于浏览器本地存储 (`localStorage`) 中[^1]。 2. **错误处理**: 当接收到服务器返回的状态码为 401(未授权)时,会判断是否需要刷新 Token 并执行相应操作[^2]。 3. **防重复刷新**: 引入布尔变量 `isRefreshing` 来确保同一时间只有一个 Token 刷新过程正在运行[^3]。 4. **请求排队机制**: 如果有多个请求在同一时刻发现 Token 失效,则这些请求会被加入队列等待刷新完成后再依次重试[^4]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值