dar*_*mnx 5 javascript security authentication authorization jwt
我遇到了一个问题,我在任何地方都找不到安全第一和可维护的答案。
想象一个仪表板同时执行多个查询,您如何以干净和标准的方式处理refresh_tokens?
堆栈是(即使堆栈在这里无关紧要):
后端 - 带有 JWT 令牌认证的 Laravel 前端 - 带有 axios 的 Vue JS 用于 API 调用
端点:
JWT 刷新工作流
axios.get('/auth/login').then(res => setTokenAndUser(res))// ... just some pseudo code
userDidAction() {
axios.get('/statistics').then(res => handleThis(res.data));
axios.get('/other-statistics').then(res => handleThat(res.data));
axios.get('/event-more-statistics').then(res => handleThisAgain(res.data));
axios.get('/final-statistics').then(res => handleThatAgain(res.data));
}
// ...
Run Code Online (Sandbox Code Playgroud)
这是 SPA 和 SaaS 应用程序中非常常见的场景。具有多个异步 API 调用不是边缘情况。
我在这里有什么选择?
我目前的想法是使用以下工作流程使access_token持续 3 天,使refresh_token持续一个月:
这使得refresh_token在网络上的传播更少,并使并行失败成为不可能,因为我们仅在前端加载时才更改令牌,因此令牌在失败前至少会存活 12 小时。
尽管这个解决方案有效,我正在寻找一种更安全/标准的方式,有什么线索吗?
这是我在应用程序中遇到的情况以及解决方法:
我将令牌数据保存在会话存储中,并每次使用刷新令牌 API 响应进行更新
我在一个页面中有三个 get 请求,我希望这种行为是,当令牌过期时,只有其中一个可以调用刷新令牌 API,而其他请求则必须等待响应,当刷新令牌承诺得到解决时,这三个请求都得到解决应使用更新的令牌数据重复失败的请求
这是 vuex 设置:
// here is the state to check if there is a refresh token request proccessing or not
export const state = () => ({
isRefreshing: false,
});
// mutation to update the state
export const mutations = {
SET_IS_REFRESHING(state, isRefreshing) {
state.isRefreshing = isRefreshing;
},
};
// action to call the mutation with a false or true payload
export const actions = {
setIsRefreshing({ commit }, isRefreshing) {
commit('SET_IS_REFRESHING', isRefreshing);
},
};
Run Code Online (Sandbox Code Playgroud)
这是 axios 设置:
import { url } from '@/utils/generals';
// adding axios instance as a plugin to nuxt app (nothing to concern about!)
export default function ({ $axios, store, redirect }, inject) {
// creating axios instance
const api = $axios.create({
baseURL: url,
});
// setting the authorization header from the data that is saved in session storage with axios request interceptor
api.onRequest((req) => {
if (sessionStorage.getItem('user'))
req.headers.authorization = `bearer ${
JSON.parse(sessionStorage.getItem('user')).accessToken
}`;
});
// using axios response interceptor to handle the 401 error
api.onResponseError((err) => {
// function that redirects the user to the login page if the refresh token request fails
const redirectToLogin = function () {
// some code here
};
if (err.response.status === 401) {
// failed API call config
const config = err.config;
// checks the store state, if there isn't any refresh token proccessing attempts to get new token and retry the failed request
if (!store.state.refreshToken.isRefreshing) {
return new Promise((resolve, reject) => {
// updates the state in store so other failed API with 401 error doesnt get to call the refresh token request
store.dispatch('refreshToken/setIsRefreshing', true);
let refreshToken = JSON.parse(sessionStorage.getItem('user'))
.refreshToken;
// refresh token request
api
.post('token/refreshToken', {
refreshToken,
})
.then((res) => {
if (res.data.success) {
// update the session storage with new token data
sessionStorage.setItem(
'user',
JSON.stringify(res.data.customResult)
);
// retry the failed request
resolve(api(config));
} else {
// rediredt the user to login if refresh token fails
redirectToLogin();
}
})
.catch(() => {
// rediredt the user to login if refresh token fails
redirectToLogin();
})
.finally(() => {
// updates the store state to indicate the there is no current refresh token request and/or the refresh token request is done and there is updated data in session storage
store.dispatch('refreshToken/setIsRefreshing', false);
});
});
} else {
// if there is a current refresh token request, it waits for that to finish and use the updated token data to retry the API call so there will be no Additional refresh token request
return new Promise((resolve, reject) => {
// in a 100ms time interval checks the store state
const intervalId = setInterval(() => {
// if the state indicates that there is no refresh token request anymore, it clears the time interval and retries the failed API call with updated token data
if (!store.state.refreshToken.isRefreshing) {
clearInterval(intervalId);
resolve(api(config));
}
}, 100);
});
}
}
});
// injects the axios instance to nuxt context object (nothing to concern about!)
inject('api', api);
}
Run Code Online (Sandbox Code Playgroud)
这是网络选项卡中所示的情况:
正如您在这里看到的,有 3 个失败的请求,并出现 401 错误,然后有一个刷新令牌请求,之后所有失败的请求都会使用更新的令牌数据再次调用
| 归档时间: |
|
| 查看次数: |
2131 次 |
| 最近记录: |