Делал свою реализацию обновления токенов для JWT-аутентификации, столкнулся с проблемой синхронизации обновления между несколькими открытыми вкладками.
Суть проблемы: если использовать для синхронизации localStorage (флаг isRefreshing, говорящий о том, что в данный момент токен уже обновляется, следовательно остальные запросы должны подождать и полететь на сервак с новым токеном через некоторое время), то можно поймать ситуацию, когда после устаревания токена запрос может полететь из двух вкладок одновременно.
Фактически, вторая вкладка не успевает отследить изменение флага в localStorage, которое было сделано первой вкладкой, из-за чего вторая вкладка запускает еще одно обновление токенов, что приводит к некорректной работе программы.
if (!isRefreshing()) { // проверка на то, обновляется ли токен в данный момент
// проверка в другой вкладке фактически будет выполнена в данный момент времени
updateRefreshFlag(true);
// чтобы код был безопасным, вторая вкладка должна делать проверку в этот момент исполнения кода в первой вкладке
// некоторый код обновления токена
}
function isRefreshing() {
return JSON.parse(document.localStorage.getItem('is_refreshing'))
}
function updateRefreshFlag(v) {
document.localStorage.setItem('is_refreshing', v)
}
Проблему можно воспроизвести следующим образом:
1.
создать две страницы со следующим кодом и открыть их в chrome в разных вкладках одного окна
2.
воспользоваться расширением " reload all tabs" и поперезагружать их несколько раз. Мы будем видеть в консоли flag value: true или flag value: false, что воспроизводит проблему
Первая вкладка
<body>
<button id="btn">
toogle flag
</button>
<script>
const btn = document.getElementById('btn')
btn.addEventListener('click', () => {
setFlag(!getFlag())
});
setFlag(true)
function setFlag(value) {
window.localStorage.setItem('flag', value)
}
function getFlag() {
return JSON.parse(window.localStorage.getItem('flag'))
}
</script>
</body>
Вторая вкладка
<body>
<script>
performAction()
function setFlag(value) {
window.localStorage.setItem('flag', value)
}
function getFlag() {
return JSON.parse(window.localStorage.getItem('flag'))
}
function performAction() {
const flagValue = getFlag()
console.log('flag value: ', flagValue)
if (flagValue) {
console.log('flag value === true, set false then')
setFlag(false)
}
}
</script>
</body>
Возникает проблема безопасной записи и считывания данных в localStorage и мне интересны способы её решения.