Когда срок действия токена истекает, я хочу получить новый токен на основе refresh_token. Я читал, что это можно получить с помощью axios.interceptors.
Пожалуйста, проверьте, если:
Items.axios.interceptors.response присваивается переменной interceptor. Что мне делать с этой переменной?В дополнение к `axios.interceptors' мне нужно получить новый токен. Токен действителен в течение 24 часов.
Код здесь: https://stackblitz.com/edit/react-pkea41
import axios from 'axios';
axios.defaults.baseURL = localStorage.getItem('domain');
const interceptor = axios.interceptors.response.use(
response => response,
error => {
// Reject promise if usual error
if (errorResponse.status !== 401) {
return Promise.reject(error);
}
/*
* When response code is 401, try to refresh the token.
* Eject the interceptor so it doesn't loop in case
* token refresh causes the 401 response
*/
axios.interceptors.response.eject(interceptor);
return axios.post('/api/refresh_token', {
'refresh_token': JSON.parse(localStorage.getItem('token'))['refresh_token']
}).then(response => {
/*saveToken();*/
localStorage.setItem('token', JSON.stringify(response.data));
error.response.config.headers['Authorization'] = 'Bearer ' + response.data.access_token;
return axios(error.response.config);
}).catch(error => {
/*destroyToken();*/
localStorage.setItem('token', '');
this.router.push('/login');
return Promise.reject(error);
}).finally(createAxiosResponseInterceptor);
}
);
class Items extends Component {
constructor (props) {
super(props);
this.state = {
}
}
render () {
return (
<div >
</div>
)
}
}
render(<Items />, document.getElementById('root'));



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Please check if I have correctly configured axios.interceptors.
Из того, что я вижу, конфигурация выглядит нормально, так как она такая же, как и в этом ответе https://stackoverflow.com/a/53294310/4229159.
Have I placed it in the right place, i.e. above theItems class. ?
Это то, на что я не могу ответить, каждое приложение отличается, это не лучшее место для этого, но для примера может подойти. Однако в вашем приложении он должен быть вместе со всеми вызовами API (например)
axios.interceptors.response is assigned to theinterceptor variable. What should I do with this variable?
Как видите, переменная, которая получила ответ от вызова /refresh_token, назначена config.headers['Authorization'] = 'Bearer ' + response.data.access_token;, если ваш бэкенд читает оттуда значение аутентификации, все должно быть в порядке.
I have to wait 24 hours to test whether it works, or is it possible in a different way, faster?
Вам следует подождать, если бэкэнд не сможет это изменить, и истечет срок действия токена за меньшее время (например, через 5 или 2 минуты).
Where should I put 'client_id', 'secret_id', 'grant_type'?
Похоже, что бэкэнд должен иметь это, если только они не являются общедоступными ... Вероятно, вам лучше всего знать, относится ли это к конфигурации для вызова или вы проходите аутентификацию с ними. Если вы проходите аутентификацию с ними, и именно они предоставляют вам токен, то вам не следует размещать его на стороне клиента, так как это представляет угрозу безопасности.
Это то, что я сделал раньше. Ваша конфигурация немного отличается от моей.
const baseURL = localStorage.getItem('domain');
const defaultOptions = {
baseURL,
method: 'get',
headers: {
'Content-Type': 'application/json',
}
};
// Create Instance
const axiosInstance = axios.create(defaultOptions);
// Get token from session
const accessToken = ...
// Set the auth token for any request
instance.interceptors.request.use(config => {
config.headers.Authorization = accessToken ? `Bearer ${accessToken}` : '';
return config;
});
// Last step: handle request error general case
instance.interceptors.response.use(
response => response,
error => {
// Error
const { config, response: { status } } = error;
if (status === 401) {
// Unauthorized request: maybe access token has expired!
return refreshAccessToken(config);
} else {
return Promise.reject(error);
}
}
});
Я думаю, что эту часть следует отделить от Компонентов - она будет размещена на хелперах или утилитах.
Кроме того, вам придется ждать 24 часа, потому что метод refreshToken() никогда не вызывается раньше 24 часов.
Вам не нужно обрабатывать client_id, secret_id, grant_type прямо здесь.
Что это означает? Зачем мне создавать новый экземпляр. Пожалуйста, объясните мне это? const axiosInstance = axios.create(defaultOptions); А как насчет некоторых failedQueue? Нужно ли мне создавать массив failedQueue? И где в этом коде снова устанавливается токен в localstorage? Ведь мне нужно установить токен в localStorage?
Я прикреплю новый файл на Google Диске, а затем проверю этот файл. Вот ссылка: docs.google.com/document/d/…
Please check if I have correctly configured axios.interceptors.
Я думаю, это работает. Но я предлагаю вам тщательно протестировать его. Это хорошая статья для ссылки https://blog.liplex.de/axios-interceptor-to-refresh-jwt-token-after-expiration/
Have I placed it in the right place, i.e. above theItems class. ?
Вы должны создать сервисную функцию для переноса конфигураций Axios и API и, конечно же, перехватчика.
axios.interceptors.response is assigned to the interceptor variable. What should I do with this variable?
Это просто переменная, используемая для определения перехватчика. Плевать на это. Если вы хотите избежать его назначения, просто сделайте это внутри такой функции, как эта Автоматическое обновление токена доступа с помощью перехватчиков в axios
I have to wait 24 hours to test whether it works, or is it possible in a different way, faster?
Вы можете изменить токен, сохраненный в вашем локальном хранилище, и сделать это
Where should I put 'client_id', 'secret_id', 'grant_type'?
Если вы храните его внутри localStorage, он доступен любому сценарию на вашей странице (что так же плохо, как и звучит, поскольку атака XSS может позволить внешнему злоумышленнику получить доступ к токену).
Не храните его в локальном хранилище (или хранилище сеансов). Если какой-либо из сценариев 3-й части, которые вы включаете на свою страницу, будет скомпрометирован, он может получить доступ ко всем токенам ваших пользователей.
JWT должен храниться внутри файла cookie HttpOnly, особого типа файла cookie, который отправляется только в HTTP-запросах на сервер и никогда не доступен (как для чтения, так и для записи) из JavaScript, запущенного в браузере.
1) Конфигурация выглядит нормально для меня. Но ваше решение не будет работать, когда есть несколько параллельных запросов, и все они пытаются обновить токен аутентификации одновременно. Поверьте мне, это проблема, которую действительно трудно точно определить. Так что лучше прикрыться заранее.
2) Нет. Не то место. Создайте отдельную службу (я называю ее api.service) и выполняйте всю коммутацию сети/API, используя ее.
3) Переменная-перехватчик не используется. Вы можете не присваивать его переменной.
4) Если у вас есть контроль над API, вы можете немного уменьшить время ожидания. Также я думаю, что 24 часа слишком долго. Иначе без вариантов, я думаю.
5) Не уверен, что вам придется иметь с ними дело.
Ниже приведен рабочий код api.service.ts. Возможно, вам придется изменить несколько вещей здесь и там, чтобы соответствовать вашему приложению. Если вы четко усвоите концепцию, это не составит труда. Также он охватывает проблему с несколькими параллельными запросами.
import * as queryString from 'query-string';
import axios, { AxiosRequestConfig, Method } from 'axios';
import { accountService } from '../account.service'; //I use account service to authentication related services
import { storageService } from './storage.service'; //I use storage service to keep the auth token. inside it it uses local storage to save values
var instance = axios.create({
baseURL: 'your api base url goes here',
});
axios.defaults.headers.common['Content-Type'] = 'application/json';
export const apiService = {
get,
post,
put,
patch,
delete: deleteRecord,
delete2: deleteRecord2
}
function get<T>(controller: string, action: string = '', urlParams: string[] = [], queryParams: any = null) {
return apiRequest<T>('get', controller, action, null, urlParams, queryParams);
}
function post<T>(controller: string, action: string = '', data: any, urlParams: string[] = [], queryParams: any = null) {
return apiRequest<T>('post', controller, action, data, urlParams, queryParams);
}
function put<T>(controller: string, action: string = '', data: any, urlParams: string[] = [], queryParams: any = null) {
return apiRequest<T>('put', controller, action, data, urlParams, queryParams);
}
function patch<T>(controller: string, action: string = '', data: any, urlParams: string[] = [], queryParams: any = null) {
return apiRequest<T>('patch', controller, action, data, urlParams, queryParams);
}
function deleteRecord(controller: string, action: string = '', urlParams: string[] = [], queryParams: any = null) {
return apiRequest<any>('delete', controller, action, null, urlParams, queryParams);
}
function deleteRecord2<T>(controller: string, action: string = '', urlParams: string[] = [], queryParams: any = null) {
return apiRequest<T>('delete', controller, action, null, urlParams, queryParams);
}
function apiRequest<T>(method: Method, controller: string, action: string = '', data: any, urlParams: string[] = [], queryParams: any = null) {
var url = createUrl(controller, action, urlParams, queryParams);
var options = createRequestOptions(url, method, data);
return instance.request<T>(options)
.then(res => res && res.data)
.catch(error => {
if (error.response) {
//handle error appropriately: if you want to display a descriptive error notification this is the place
} else {
//handle error appropriately: if you want to display a a generic error message
}
throw error;
});
}
function createUrl(controller: string, action: string = '', urlParams: string[] = [], queryParams: any = null) {
let url = controller + (action ? '/' + action : '');
urlParams.forEach(param => {
url += '/' + param;
});
let params = '';
if (queryParams) {
params += '?' + queryString.stringify(queryParams);
}
return url += params;
}
function createRequestOptions(url: string, method: Method, data: any, responseType?: any) {
var authToken = storageService.getAuthToken();
var jwtToken = authToken != null ? authToken.authToken : '';
var options: AxiosRequestConfig = {
url,
method,
data,
headers: {
'Authorization': 'bearer ' + jwtToken
},
}
if (responseType) {
options.responseType = responseType;
}
return options;
}
let isRefreshing = false;
let failedQueue: any[] = [];
const processQueue = (error: any, token: string = '') => {
failedQueue.forEach(prom => {
if (error) {
prom.reject(error);
} else {
prom.resolve(token);
}
});
failedQueue = [];
}
instance.interceptors.response.use(undefined, (error) => {
const originalRequest = error.config;
if (originalRequest && error.response && error.response.status === 401 && !originalRequest._retry) {
if (isRefreshing) {
return new Promise(function (resolve, reject) {
failedQueue.push({ resolve, reject })
}).then(authToken => {
originalRequest.headers.Authorization = 'bearer ' + authToken;
return axios(originalRequest);
}).catch(err => {
return err;
})
}
originalRequest._retry = true;
isRefreshing = true;
return new Promise(function (resolve, reject) {
accountService.refreshToken()
.then(result => {
if (result.succeeded) {
originalRequest.headers.Authorization = 'bearer ' + result.authToken;
axios(originalRequest).then(resolve, reject);
processQueue(null, result.authToken);
} else {
reject(error);
}
}).catch((err) => {
processQueue(err);
reject(err);
}).then(() => { isRefreshing = false });
});
}
return Promise.reject(error);
});
Ваше здоровье,
Я думаю, что вопрос слишком широк, что такое именно то, что вы пытаетесь сделать. Что работает, а что нет? В вашем ответе слишком много вопросов и было бы лучше систематизировать их, чтобы не терять людей во время чтения.