Node.js 为什么axios拦截器经常失败?我正在尝试获取刷新令牌并重新请求原始请求
我使用访问和刷新令牌,它们都存储在cookie中。我正在使用axios拦截器捕获401错误导致的请求失败,如果是这样,它将获取一个新令牌,然后重试 但似乎有20%的时间,特别是在mobile safari上,即使我有一个有效的刷新令牌,这也会失败,并导致我注销 这很奇怪,因为在后端控制台中,我可以看到一个新的访问和刷新令牌被发送。如果我立即刷新页面,它会让我重新登录 对于前端,对于第一个请求,它将显示401错误,但即使我告诉它尝试多次,它也不会显示任何其他错误。它似乎几乎跳过了重试原始请求,而是直接进入错误部分。我还可以在“网络”选项卡中看到,它只发出一次请求,这是失败的原始请求 我还可以从后端看到,它从未命中原始的auth中间件,这让我更相信它有时没有尝试原始请求 我同时发送多个请求,如果访问令牌过期,它将导致每个请求获得刷新令牌,我不确定这是否也是一个问题,但它似乎在大多数情况下都有效 我将发布下面的代码,以显示我是如何做到这一点的。我正在运行最新的Axios版本,节点14.5.0和14.7.1,因此一切都是最新的 Axios拦截器代码,可能值得注意的是错误函数在哪里,它说“这可能会转换为异步”,所以我不确定这是否是一个潜在问题:Node.js 为什么axios拦截器经常失败?我正在尝试获取刷新令牌并重新请求原始请求,node.js,reactjs,express,axios,jwt,Node.js,Reactjs,Express,Axios,Jwt,我使用访问和刷新令牌,它们都存储在cookie中。我正在使用axios拦截器捕获401错误导致的请求失败,如果是这样,它将获取一个新令牌,然后重试 但似乎有20%的时间,特别是在mobile safari上,即使我有一个有效的刷新令牌,这也会失败,并导致我注销 这很奇怪,因为在后端控制台中,我可以看到一个新的访问和刷新令牌被发送。如果我立即刷新页面,它会让我重新登录 对于前端,对于第一个请求,它将显示401错误,但即使我告诉它尝试多次,它也不会显示任何其他错误。它似乎几乎跳过了重试原始请求,而是
const axiosNoRetry = axios.create();
axios.interceptors.response.use( (response) => {
console.log("axios interceptor")
return response;
}, (error) => {
const originalRequest = error.config;
if (originalRequest.ran === 3) {
console.log("Max retry count", error.config.url);
return Promise.reject(error);
}
console.log("axios retry count", originalRequest.ran);
if (error.response.status !== 401) {
console.log("error does not equal 401");
return new Promise((resolve, reject) => {
reject(error);
});
}
if (error.config.url === "/user-service/get-token") {
console.log("error url equal to refresh token route")
return new Promise((resolve, reject) => {
reject(error);
});
}
return axiosNoRetry.post("/user-service/get-token").then((cookieResponse) => {
console.log("cookie status", cookieResponse.status);
if (cookieResponse.status === 201) {
originalRequest.ran = originalRequest.ran ? originalRequest.ran + 1 : 1;
return axios(originalRequest);
} else {
return Promise.reject(error);
}
})
});
export default axios;
这是我的身份验证中间件,它保护/user-service/user路由以检查是否有人登录:
const auth = async(req: RequestType, res: Response, next: NextFunction) => {
try {
console.log("\nAUTH NO EMAIL");
const accessToken = req.cookies["access-token"];
if (!accessToken) throw new Error("No Access Token");
const decoded = jwt.verify(accessToken, env.passwordAccess!) as jwtType;
const user = decoded.user;
if (!user) throw new Error("No User");
req.user = user;
next();
} catch (e) {
console.log("\nAuthorization Middleware No Email Verifiaction Error:",
e.message);
res.status(401).send("Error Authenticating");
}
}
export default auth;
以下是检查是否存在有效刷新令牌的刷新中间件:
const authRefresh = async(req: RequestType, res: Response, next: NextFunction) => {
try {
console.log("\nrefresh token auth")
const refreshToken = req.cookies["refresh-token"];
if (!refreshToken) throw new Error("No Refresh Token");
const decoded = jwt.verify(refreshToken, env.passwordRefresh!) as jwtType;
const time = decoded.time;
const user = await User.findById(new ObjectID(decoded._id));
if (!user) throw new Error("No User");
const encrpytionKey = user.getEncryptionKey();
const encryptedToken = user.encryptToken(refreshToken, encrpytionKey,
decoded.iv);
let tokenFound = false;
for (let i = 0; i < user.tokens.length; i++) {
const currentEncryptedToken = user.tokens[i].token;
if (currentEncryptedToken === encryptedToken) {
tokenFound = true;
removeOldTokens(user._id, req.clientIp, time);
break;
}
}
if (!tokenFound) throw new Error("Refresh Token Not Found");
req.user = user;
next();
} catch (e) {
console.log("\nAuthorization Refresh Middleware Error:", e.message);
res.status(401).send("Error Refreshing Token");
}
}
因此,正如您所看到的,“AUTH NO EMAIL”路由只被调用一次,它将不再被调用,并且刷新令牌将被发送
以下是我在前端控制台中看到的内容:
[Error] Failed to load resource: the server responded with a status of 401 (Unauthorized) (user, line 0)
[Log] axios retry count – undefined (bundle.js, line 323)
[Error] Failed to load resource: the server responded with a status of 401 (Unauthorized) (list, line 0)
[Log] axios retry count – undefined (bundle.js, line 323)
[Log] cookie status – 201 (bundle.js, line 323, x2)
[Log] axios retry count – 1 (bundle.js, line 323)
[Log] cookie status – 201 (bundle.js, line 323)
[Log] axios retry count – 2 (bundle.js, line 323)
[Log] cookie status – 201 (bundle.js, line 323)
[Log] Max retry count – "http://192.168.0.28:3000/user-service/user" (bundle.js, line 323)
[Log] login check error (4) (bundle.js, line 323)
Error: Request failed with status code 401
"Error Authenticating"
undefined
{data: "Error Authenticating", status: 401, statusText: "Unauthorized", headers: Object, config: Object, …}
[Log] axios interceptor (bundle.js, line 323)
我不知道为什么它在底部写着“axios interceptor”,正如你在我上面的代码中看到的,这应该只在请求成功时才显示出来,但它似乎没有成功,因为我再也看不到该请求,并且它将我注销。您可以看到它也返回了一个带有登录检查错误的错误
我不确定是否需要,但我也会发布前端的登录检查代码:
export const startLoginCheck = (currentRoute) => {
return (dispatch) => {
axios.get(currentURL+"/user-service/user").then((response) => {
console.log("USER SERVICE LOGIN CHECK RESPONSE")
const emailVerified = response.data.emailVerified;
const id = response.data._id;
env.googleDriveEnabled = response.data.googleDriveEnabled;
env.s3Enabled = response.data.s3Enabled;
env.activeSubscription = response.data.activeSubscription;
env.emailAddress = response.data.email;
env.name = response.data.name || ""
if (emailVerified) {
dispatch(setLoginFailed(false))
dispatch(login(id))
history.push(currentRoute);
} else {
console.log("Email Not Verified")
dispatch(setLoginFailed("Unverified Email", 404))
}
}).catch((err) => {
console.log("login check error", err, err.response.data, err.data,
err.response);
// window.localStorage.removeItem("token")
dispatch(setLoginFailed("Login Expired"))
// history.push("/login")
})
}
}
如果有人有任何想法,我会很高兴听到,这让我发疯
[Error] Failed to load resource: the server responded with a status of 401 (Unauthorized) (user, line 0)
[Log] axios retry count – undefined (bundle.js, line 323)
[Error] Failed to load resource: the server responded with a status of 401 (Unauthorized) (list, line 0)
[Log] axios retry count – undefined (bundle.js, line 323)
[Log] cookie status – 201 (bundle.js, line 323, x2)
[Log] axios retry count – 1 (bundle.js, line 323)
[Log] cookie status – 201 (bundle.js, line 323)
[Log] axios retry count – 2 (bundle.js, line 323)
[Log] cookie status – 201 (bundle.js, line 323)
[Log] Max retry count – "http://192.168.0.28:3000/user-service/user" (bundle.js, line 323)
[Log] login check error (4) (bundle.js, line 323)
Error: Request failed with status code 401
"Error Authenticating"
undefined
{data: "Error Authenticating", status: 401, statusText: "Unauthorized", headers: Object, config: Object, …}
[Log] axios interceptor (bundle.js, line 323)
export const startLoginCheck = (currentRoute) => {
return (dispatch) => {
axios.get(currentURL+"/user-service/user").then((response) => {
console.log("USER SERVICE LOGIN CHECK RESPONSE")
const emailVerified = response.data.emailVerified;
const id = response.data._id;
env.googleDriveEnabled = response.data.googleDriveEnabled;
env.s3Enabled = response.data.s3Enabled;
env.activeSubscription = response.data.activeSubscription;
env.emailAddress = response.data.email;
env.name = response.data.name || ""
if (emailVerified) {
dispatch(setLoginFailed(false))
dispatch(login(id))
history.push(currentRoute);
} else {
console.log("Email Not Verified")
dispatch(setLoginFailed("Unverified Email", 404))
}
}).catch((err) => {
console.log("login check error", err, err.response.data, err.data,
err.response);
// window.localStorage.removeItem("token")
dispatch(setLoginFailed("Login Expired"))
// history.push("/login")
})
}
}