Issue
,将无法得到答复。哪个版本号?
异常堆栈是什么?
⚠⚠ 必须提供完整可运行且包含错误的
Git
仓库 DEMO,DEMO 提供最简单的错误逻辑代码,否则将无法得到答复。⚠⚠
您的代码下载地址?
show me code 啦。
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。
我参与的客户项目有33个,都交付上线了,全部都是自动刷新token的方式,目前没有遇到过这样的情况,所以希望提供例子。请提供例子和图文证明问题,不能只发一个标题哈
而且参考我这个:https://gitee.com/dotnetchina/Furion/blob/v4/clients/axios_vue_react/axios-utils.ts
譬如 我申请下来的是:
令牌:2022年10月21日 09:00:00
过期时间 :2022年10月21日 09:01:00
刷新令牌过期时间:2022年10月21日 09:02:00
当时间推移到2022年10月21日 09:01:30 时 此时令牌已经过期,所以携带了刷新令牌,但是访问的API是受限的,403
此时我的想法是,服务端就应该返回403 且携带新的令牌和刷新令牌
但真实返回的却是401
谢谢大佬之前的解答
我再补充一下:如果访问的接口是正常返回200状态,此情况不会发生!
可以提供一个例子上来,测试了一个小时都没测试出问题,太浪费时间了。
测试可以无限续期:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmIjoiZXlKaGJHY2lPaUpJVXpJMU5pSXNJblI1Y0NJNklrcFhWQ0o5IiwiZSI6ImE2MUVPVHVUSEczYnp4clRBVkgwazJ3VC1oLWhfUnhveEFqTm5ReEQzX28iLCJzIjo0NywibCI6NiwiayI6ImlPakUyTiIsImlhdCI6MTY2NjMzNjc0MCwibmJmIjoxNjY2MzM2NzQwLCJleHAiOjE2NjYzMzY4NjAsImlzcyI6ImRvdG5ldGNoaW5hIiwiYXVkIjoicG93ZXJieSBGdXJpb24ifQ.zmEdsUehBV_KQzqJUa689iPtQTiKN8a_D794Nvkokgs
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VySWQiOjEsIlVzZXJOYW1lIjoiYWRtaW4iLCJpYXQiOjE2NjYzMzY3NDAsIm5iZiI6MTY2NjMzNjc0MCwiZXhwIjoxNjY2MzM2ODAwLCJpc3MiOiJkb3RuZXRjaGluYSIsImF1ZCI6InBvd2VyYnkgRnVyaW9uIn0.a61EOTuTHG3bzxrTAVH0k2wT-h-h_RxoxAjNnQxD3_o
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmIjoiZXlKaGJHY2lPaUpJVXpJMU5pSXNJblI1Y0NJNklrcFhWQ0o5IiwiZSI6ImE2MUVPVHVUSEczYnp4clRBVkgwazJ3VC1oLWhfUnhveEFqTm5ReEQzX28iLCJzIjo0NywibCI6NiwiayI6ImlPakUyTiIsImlhdCI6MTY2NjMzNjc0MCwibmJmIjoxNjY2MzM2NzQwLCJleHAiOjE2NjYzMzY4NjAsImlzcyI6ImRvdG5ldGNoaW5hIiwiYXVkIjoicG93ZXJieSBGdXJpb24ifQ.zmEdsUehBV_KQzqJUa689iPtQTiKN8a_D794Nvkokgs
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VySWQiOjEsIlVzZXJOYW1lIjoiYWRtaW4iLCJpYXQiOjE2NjYzMzY3NDAsIm5iZiI6MTY2NjMzNjc0MCwiZXhwIjoxNjY2MzM2ODAwLCJpc3MiOiJkb3RuZXRjaGluYSIsImF1ZCI6InBvd2VyYnkgRnVyaW9uIn0.a61EOTuTHG3bzxrTAVH0k2wT-h-h_RxoxAjNnQxD3_o
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmIjoiZXlKaGJHY2lPaUpJVXpJMU5pSXNJblI1Y0NJNklrcFhWQ0o5IiwiZSI6ImZSQ3lYVzBkejJUd3pnbzlvTFQxSlpEZjRRbUo4UGQ4VXJGN2lfLW8tR0EiLCJzIjoyNywibCI6MTIsImsiOiJsSWpvaVlXUnRhVzQiLCJpYXQiOjE2NjYzMzY4MzIsIm5iZiI6MTY2NjMzNjgzMiwiZXhwIjoxNjY2MzM2OTUyLCJpc3MiOiJkb3RuZXRjaGluYSIsImF1ZCI6InBvd2VyYnkgRnVyaW9uIn0.tPzQ2J1r60cDy47selYfPSwzQvQAztD4iDtcDfjAlP8
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VySWQiOjEsIlVzZXJOYW1lIjoiYWRtaW4iLCJleHAiOjE2NjYzMzY4OTIsIm5iZiI6MTY2NjMzNjgzMiwiaWF0IjoxNjY2MzM2ODMyLCJpc3MiOiJkb3RuZXRjaGluYSIsImF1ZCI6InBvd2VyYnkgRnVyaW9uIn0.fRCyXW0dz2Twzgo9oLT1JZDf4QmJ8Pd8UrF7i_-o-GA
我个人认为是你的前端出问题了,没有处理好。
你可以通过 postman 的方式测试。
令牌未过期 访问没有权限访问的资源
令牌已过期,访问没有权限访问的资源,但是携带了刷新令牌.服务端也返回了新的令牌和刷新令牌,但是状态码为401
200情况下,是正常的!
https://gitee.com/yjtyejintian/ademo
1先登录获取令牌
2等待1分30秒左右(令牌过期时间为1分钟,刷新令牌为2分钟)
3点击访问 api403 按钮 状态码为401 ,此时它其实是携带了刷新令牌的!
代码链接:
https://gitee.com/yjtyejintian/ademo
麻烦了!
直接将时间戳转换成时间显示更直观些
已修正:9c7f9ef
明天发布新版本。
测试结果:测试通过
另外 index.html
内容也存在问题。
index.html
内容纠正:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<button class="loginbtn">登录</button>
<button class="api200">访问api 200</button>
<button class="api403">访问api 403</button>
<div>
<ul class="msg">
</ul>
</div>
<script src="./axios.js"></script>
<script>
const loginbtn = document.querySelector('.loginbtn');
const api200 = document.querySelector('.api200');
const api403 = document.querySelector('.api403');
const msgul = document.querySelector('.msg');
const loginurl = 'http://localhost:5000/login';
const api200url = 'http://localhost:5000/test1';
const api403url = 'http://localhost:5000/test2';
const accessTokenKey = "access-token";
const refreshAccessTokenKey = `x-${accessTokenKey}`;
const throwError = (message) => {
throw new Error(message);
};
axios.interceptors.request.use(
(conf) => {
// 获取本地的 token
const accessToken = window.localStorage.getItem(accessTokenKey);
if (accessToken) {
// 将 token 添加到请求报文头中
conf.headers["Authorization"] = `Bearer ${accessToken}`;
// 判断 accessToken 是否过期
const jwt = decryptJWT(accessToken);
const exp = getJWTDate(jwt.exp);
if (new Date() >= exp) {
// 获取刷新 token
const refreshAccessToken = window.localStorage.getItem(
refreshAccessTokenKey
);
// 携带刷新 token
if (refreshAccessToken) {
conf.headers["X-Authorization"] = `Bearer ${refreshAccessToken}`;
}
}
}
// 这里编写请求拦截代码 =========================================
return conf;
},
(error) => {
// 处理请求错误
if (error.request) {
}
// 这里编写请求错误代码
return Promise.reject(error);
}
);
// axios 响应拦截
axios.interceptors.response.use(
(res) => {
// 检查并存储授权信息
checkAndStoreAuthentication(res);
// 处理规范化结果错误
const serve = res.data;
console.log(serve);
if (serve && serve.hasOwnProperty("errors") && serve.errors) {
// 处理规范化 401 授权问题
if (serve.errors === "401 Unauthorized") {
clearAccessTokens();
}
throwError(
!serve.errors
? "Request Error."
: typeof serve.errors === "string"
? serve.errors
: JSON.stringify(serve.errors)
);
return;
}
// 这里编写响应拦截代码 =========================================
return res;
},
(error) => {
// 处理响应错误
if (error.response) {
// 获取响应对象并解析状态码
const res = error.response;
const status = res.status;
// 检查并存储授权信息
checkAndStoreAuthentication(res);
// 检查 401 权限
if (status === 401) {
clearAccessTokens();
}
}
// 这里编写响应错误代码
return Promise.reject(error);
}
);
const clearAccessTokens = () => {
window.localStorage.removeItem(accessTokenKey);
window.localStorage.removeItem(refreshAccessTokenKey);
// 刷新浏览器
// window.location.reload();
// 这里可以添加清除更多 Key =========================================
};
function checkAndStoreAuthentication(res) {
// 读取响应报文头 token 信息
var accessToken = res.headers[accessTokenKey];
var refreshAccessToken = res.headers[refreshAccessTokenKey];
// 判断是否是无效 token
if (accessToken === "invalid_token") {
clearAccessTokens();
}
// 判断是否存在刷新 token,如果存在则存储在本地
else if (
refreshAccessToken &&
accessToken &&
accessToken !== "invalid_token"
) {
window.localStorage.setItem(accessTokenKey, accessToken);
window.localStorage.setItem(refreshAccessTokenKey, refreshAccessToken);
}
}
function decryptJWT(token) {
token = token.replace(/_/g, "/").replace(/-/g, "+");
var json = decodeURIComponent(escape(window.atob(token.split(".")[1])));
return JSON.parse(json);
}
/**
* 将 JWT 时间戳转换成 Date
* @description 主要针对 `exp`,`iat`,`nbf`
* @param timestamp 时间戳
* @returns Date 对象
*/
function getJWTDate(timestamp) {
return new Date(timestamp * 1000);
}
loginbtn.addEventListener('click', () => {
axios.post(loginurl, { username: 'zhangsan', password: '123456' }).then(res => {
appenMsg('获取token完成');
})
})
api200.addEventListener('click', () => {
axios.get(api200url).then(res => {
appenMsg(res.status);
}).catch(err => {
appenMsg(err.message)
})
})
api403.addEventListener('click', () => {
axios.get(api403url).then(res => {
appenMsg(res.status);
}).catch(err => {
appenMsg(err.message)
})
})
function appenMsg(msg) {
const li = document.createElement('li');
li.innerHTML = msg;
msgul.appendChild(li);
}
</script>
</body>
</html>
下个版本发布,先关闭issue,如果新版本更新后还有问题再打开,明天发布。
Furion v4.6.8 版本已发布,升级到该版本,并替换你的 index.html
即可:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<button class="loginbtn">登录</button>
<button class="api200">访问api 200</button>
<button class="api403">访问api 403</button>
<div>
<ul class="msg">
</ul>
</div>
<script src="./axios.js"></script>
<script>
const loginbtn = document.querySelector('.loginbtn');
const api200 = document.querySelector('.api200');
const api403 = document.querySelector('.api403');
const msgul = document.querySelector('.msg');
const loginurl = 'http://localhost:5000/login';
const api200url = 'http://localhost:5000/test1';
const api403url = 'http://localhost:5000/test2';
const accessTokenKey = "access-token";
const refreshAccessTokenKey = `x-${accessTokenKey}`;
const throwError = (message) => {
throw new Error(message);
};
axios.interceptors.request.use(
(conf) => {
// 获取本地的 token
const accessToken = window.localStorage.getItem(accessTokenKey);
if (accessToken) {
// 将 token 添加到请求报文头中
conf.headers["Authorization"] = `Bearer ${accessToken}`;
// 判断 accessToken 是否过期
const jwt = decryptJWT(accessToken);
const exp = getJWTDate(jwt.exp);
if (new Date() >= exp) {
// 获取刷新 token
const refreshAccessToken = window.localStorage.getItem(
refreshAccessTokenKey
);
// 携带刷新 token
if (refreshAccessToken) {
conf.headers["X-Authorization"] = `Bearer ${refreshAccessToken}`;
}
}
}
// 这里编写请求拦截代码 =========================================
return conf;
},
(error) => {
// 处理请求错误
if (error.request) {
}
// 这里编写请求错误代码
return Promise.reject(error);
}
);
// axios 响应拦截
axios.interceptors.response.use(
(res) => {
// 检查并存储授权信息
checkAndStoreAuthentication(res);
// 处理规范化结果错误
const serve = res.data;
console.log(serve);
if (serve && serve.hasOwnProperty("errors") && serve.errors) {
// 处理规范化 401 授权问题
if (serve.errors === "401 Unauthorized") {
clearAccessTokens();
}
throwError(
!serve.errors
? "Request Error."
: typeof serve.errors === "string"
? serve.errors
: JSON.stringify(serve.errors)
);
return;
}
// 这里编写响应拦截代码 =========================================
return res;
},
(error) => {
// 处理响应错误
if (error.response) {
// 获取响应对象并解析状态码
const res = error.response;
const status = res.status;
// 检查并存储授权信息
checkAndStoreAuthentication(res);
// 检查 401 权限
if (status === 401) {
clearAccessTokens();
}
}
// 这里编写响应错误代码
return Promise.reject(error);
}
);
const clearAccessTokens = () => {
window.localStorage.removeItem(accessTokenKey);
window.localStorage.removeItem(refreshAccessTokenKey);
// 刷新浏览器
// window.location.reload();
// 这里可以添加清除更多 Key =========================================
};
function checkAndStoreAuthentication(res) {
// 读取响应报文头 token 信息
var accessToken = res.headers[accessTokenKey];
var refreshAccessToken = res.headers[refreshAccessTokenKey];
// 判断是否是无效 token
if (accessToken === "invalid_token") {
clearAccessTokens();
}
// 判断是否存在刷新 token,如果存在则存储在本地
else if (
refreshAccessToken &&
accessToken &&
accessToken !== "invalid_token"
) {
window.localStorage.setItem(accessTokenKey, accessToken);
window.localStorage.setItem(refreshAccessTokenKey, refreshAccessToken);
}
}
function decryptJWT(token) {
token = token.replace(/_/g, "/").replace(/-/g, "+");
var json = decodeURIComponent(escape(window.atob(token.split(".")[1])));
return JSON.parse(json);
}
/**
* 将 JWT 时间戳转换成 Date
* @description 主要针对 `exp`,`iat`,`nbf`
* @param timestamp 时间戳
* @returns Date 对象
*/
function getJWTDate(timestamp) {
return new Date(timestamp * 1000);
}
loginbtn.addEventListener('click', () => {
axios.post(loginurl, { username: 'zhangsan', password: '123456' }).then(res => {
appenMsg('获取token完成');
})
})
api200.addEventListener('click', () => {
axios.get(api200url).then(res => {
appenMsg(res.status);
}).catch(err => {
appenMsg(err.message)
})
})
api403.addEventListener('click', () => {
axios.get(api403url).then(res => {
appenMsg(res.status);
}).catch(err => {
appenMsg(err.message)
})
})
function appenMsg(msg) {
const li = document.createElement('li');
li.innerHTML = msg + "~~ ~~" + (new Date().toLocaleString());
msgul.appendChild(li);
}
</script>
</body>
</html>
登录 后才可以发表评论