HM-83. Добавлены классы для работы с авторизацией (#36)
This commit is contained in:
45
src/api/AuthServiceAPI.js
Normal file
45
src/api/AuthServiceAPI.js
Normal file
@ -0,0 +1,45 @@
|
||||
import axios from 'axios';
|
||||
|
||||
import {AUTH_SERVICE, AUTH_ENDPOINTS} from './consts';
|
||||
import tokenApi from './TokenAPI';
|
||||
|
||||
/**
|
||||
* Api для выполнения запросов авторизации
|
||||
* @class AuthServiceApi
|
||||
*/
|
||||
class AuthServiceApi {
|
||||
constructor () {
|
||||
this.URL = AUTH_SERVICE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Выполняет запрос авторизации и если он успешен, то добавляет токены в память
|
||||
* @param {string} login - логин пользователя
|
||||
* @param {string} password - пароль пользователя
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
auth = (login, password) => {
|
||||
return axios.post(`${this.URL}${AUTH_ENDPOINTS.AUTH}`, {
|
||||
login,
|
||||
password,
|
||||
}).then(({data: tokens}) => {
|
||||
tokenApi.saveTokenPair(tokens);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Пытается реврешнуть пару токенов и в случае успеха обновляет их в памяти
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
refresh = () => {
|
||||
const refresh_token = tokenApi.getRefreshToken();
|
||||
return axios.post(`${this.URL}${AUTH_ENDPOINTS.REFRESH}`, {refresh_token})
|
||||
.then(({data: tokens}) => {
|
||||
tokenApi.saveTokenPair(tokens);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const authServiceApi = new AuthServiceApi();
|
||||
|
||||
export default authServiceApi;
|
||||
92
src/api/HttpAPI.js
Normal file
92
src/api/HttpAPI.js
Normal file
@ -0,0 +1,92 @@
|
||||
import {makeUrlWithQuery} from '../utils/urlUtils';
|
||||
import httpAuthApi from './HttpAuthAPI';
|
||||
|
||||
const GET = 'GET';
|
||||
const POST = 'POST';
|
||||
const PUT = 'PUT';
|
||||
const DELETE = 'DELETE';
|
||||
const HEAD = 'HEAD';
|
||||
const OPTIONS = 'OPTIONS';
|
||||
const PATH = 'PATH';
|
||||
|
||||
/**
|
||||
* Методы запросов к серверу
|
||||
* @interface MethodType
|
||||
* @type {'GET' | 'POST' | 'PUT' | 'DELETE' | 'HEAD' | 'OPTIONS' | 'PATH'}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Api для выполнения запросов к серверу
|
||||
* @class HttpApi
|
||||
*/
|
||||
class HttpApi {
|
||||
/**
|
||||
* Общий метод выполнения запроса
|
||||
* @param {MethodType} method - метод запроса
|
||||
* @param {string} url - адрес запроса
|
||||
* @param {Object} body - тело запроса
|
||||
*/
|
||||
request = (method, url, body) => {
|
||||
return httpAuthApi.request({method, url, body});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} url - адрес запроса
|
||||
* @param {Object<string, string>} query - параметры для передачи через query
|
||||
*/
|
||||
get = (url, query) => {
|
||||
return this.request(GET, makeUrlWithQuery(url, query));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} url - адрес запроса
|
||||
* @param {Object} body - тело запроса
|
||||
*/
|
||||
post = (url, body) => {
|
||||
return this.request(POST, url, body);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} url - адрес запроса
|
||||
* @param {Object} body - тело запроса
|
||||
*/
|
||||
put = (url, body) => {
|
||||
return this.request(PUT, url, body);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} url - адрес запроса
|
||||
* @param {Object<string, string>} query - параметры для передачи через query
|
||||
*/
|
||||
delete = (url, query) => {
|
||||
return this.request(DELETE, makeUrlWithQuery(url, query));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} url - адрес запроса
|
||||
* @param {Object<string, string>} query - параметры для передачи через query
|
||||
*/
|
||||
head = (url, query) => {
|
||||
return this.request(HEAD, makeUrlWithQuery(url, query));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} url - адрес запроса
|
||||
* @param {Object<string, string>} query - параметры для передачи через query
|
||||
*/
|
||||
options = (url, query) => {
|
||||
return this.request(OPTIONS, makeUrlWithQuery(url, query));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} url - адрес запроса
|
||||
* @param {Object} body - тело запроса
|
||||
*/
|
||||
path = (url, body) => {
|
||||
return this.request(PATH, url, body);
|
||||
}
|
||||
}
|
||||
|
||||
const http = new HttpApi();
|
||||
|
||||
export default http;
|
||||
109
src/api/HttpAuthAPI.js
Normal file
109
src/api/HttpAuthAPI.js
Normal file
@ -0,0 +1,109 @@
|
||||
import axios from 'axios';
|
||||
import authServiceApi from './AuthServiceAPI';
|
||||
import tokenApi from './TokenAPI';
|
||||
|
||||
/**
|
||||
* @interface RequestConfig
|
||||
* @type {Object}
|
||||
* @property {MethodType} method - метод запроса
|
||||
* @property {string} url - адрес запроса
|
||||
* @property {Object} body - тело запроса
|
||||
*/
|
||||
|
||||
/**
|
||||
* Api прикрепляет ко всем запросом заголовок с токеном авторизации. В случае, когда сервер
|
||||
* @class HttpAuthApi
|
||||
*/
|
||||
class HttpAuthApi {
|
||||
constructor () {
|
||||
this.pendingRequests = [];
|
||||
this.isPendingRefresh = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Выполняет какое-то действие над всеми запросами из очереди, пока она не закончится
|
||||
* @param {Function} handler - функция обработчик запроса
|
||||
*/
|
||||
processPendingRequests = (handler) => {
|
||||
this.isPendingRefresh = false;
|
||||
while (this.pendingRequests.length) {
|
||||
const pendingRequest = this.pendingRequests.shift();
|
||||
handler(pendingRequest);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Повторяет все запросы из очереди в порядке их поступления
|
||||
*/
|
||||
repeatRequests = () => {
|
||||
this.processPendingRequests(({resolve, reject, requestConfig}) => {
|
||||
this.request(requestConfig)
|
||||
.then(resolve)
|
||||
.catch(reject);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Выбрасывает ошибки для всех запросов из очереди
|
||||
* @param {axios.error} error - ошибка, которую возвращает axios
|
||||
*/
|
||||
rejectRequests = (error) => {
|
||||
this.processPendingRequests(({reject}) => {
|
||||
reject(error);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Перехватывает ошибку 401 (Не авторизован), другие ошибки выбрасывает дальше
|
||||
* @param {axios.error} error - ошибка, которую возвращает axios
|
||||
* @param {Function} resolve - функция промиса, которая разрешает его успешно
|
||||
* @param {Function} reject - функция промиса, которая завершает его неудачей
|
||||
* @param {RequestConfig} requestConfig - настройки для выполнения запроса
|
||||
*/
|
||||
errorHandling = (error, resolve, reject, requestConfig) => {
|
||||
if (error.response.status === 401) {
|
||||
this.isPendingRefresh = true;
|
||||
this.pendingRequests({resolve, reject, requestConfig});
|
||||
authServiceApi.refresh()
|
||||
.then(() => {
|
||||
this.repeatRequests();
|
||||
})
|
||||
.catch((refreshError) => {
|
||||
this.rejectRequests(refreshError);
|
||||
/*
|
||||
* Снять авторизацию и сделать роут
|
||||
* Сбросить все запросы с ошибкой авторизации
|
||||
*/
|
||||
});
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавляет к запросу токен в качестве заголовка
|
||||
* @param {RequestConfig} requestConfig - настройки для выполнения запроса
|
||||
*/
|
||||
addTokenToRequest = (requestConfig) => ({
|
||||
...requestConfig,
|
||||
headers: {
|
||||
'Authorization': tokenApi.getAccessToken(),
|
||||
},
|
||||
})
|
||||
|
||||
/**
|
||||
* Метод для внешнего использования, выполняет запрос к серверу, используя axios
|
||||
* @param {RequestConfig} requestConfig - настройки для выполнения запроса
|
||||
*/
|
||||
request = (requestConfig) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
axios(this.addTokenToRequest(requestConfig))
|
||||
.then((response) => resolve(response))
|
||||
.catch((error) => this.errorHandling(error, resolve, reject, requestConfig));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const httpAuthApi = new HttpAuthApi();
|
||||
|
||||
export default httpAuthApi;
|
||||
@ -1,11 +1,15 @@
|
||||
import {LOCAL_STORAGE_TYPE} from './consts';
|
||||
|
||||
/**
|
||||
* Класс работы с Local Storage браузера
|
||||
* @class LocalStorageAPI
|
||||
* @param {string} key - ключ по которому будет хранится информация
|
||||
* @param {string} key - уникальный ключ для local или session storage
|
||||
* @param {'LOCAL' | 'SESSION'} type - тип storage
|
||||
*/
|
||||
class LocalStorageAPI {
|
||||
constructor (key) {
|
||||
constructor (key, type = LOCAL_STORAGE_TYPE.LOCAL) {
|
||||
this.key = key;
|
||||
this.api = type === LOCAL_STORAGE_TYPE.LOCAL ? localStorage : sessionStorage;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -18,7 +22,7 @@ class LocalStorageAPI {
|
||||
|
||||
/**
|
||||
* Записывает данные в Local Storage по ключу из конструктора
|
||||
* @param {*} value - значение в Local Storage
|
||||
* @param {Object} value - значение в Local Storage
|
||||
*/
|
||||
createOrUpdate (value) {
|
||||
localStorage.setItem(this.key, JSON.stringify(value));
|
||||
|
||||
56
src/api/TokenAPI.js
Normal file
56
src/api/TokenAPI.js
Normal file
@ -0,0 +1,56 @@
|
||||
import LocalStorageAPI from './LocalStorageAPI';
|
||||
|
||||
const API_NAME = 'storageServiceUITokenApi';
|
||||
|
||||
/**
|
||||
* @interface TokenPair
|
||||
* @type {Object}
|
||||
* @property {string} access_token - токен авторизации
|
||||
* @property {string} refresh_token = токен рефреша авторизации
|
||||
*/
|
||||
|
||||
/**
|
||||
* Api сохраняет и возвращает токены авторизации
|
||||
* @class TokenApi
|
||||
*/
|
||||
class TokenApi {
|
||||
constructor () {
|
||||
this.localApi = new LocalStorageAPI(API_NAME);
|
||||
this.sessionApi = new LocalStorageAPI(API_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Сохраняет пару токенов
|
||||
* @param {TokenPair} tokens - пара токенов
|
||||
*/
|
||||
saveTokenPair = (tokens) => {
|
||||
this.localApi.createOrUpdate(tokens.refresh_token);
|
||||
this.sessionApi.createOrUpdate(tokens.access_token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает токен авторизации
|
||||
*/
|
||||
getAccessToken = () => {
|
||||
return this.localApi.request();
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает токен ревреша авторизации
|
||||
*/
|
||||
getRefreshToken = () => {
|
||||
return this.sessionApi.request();
|
||||
}
|
||||
|
||||
/**
|
||||
* Очищает токены из памяти
|
||||
*/
|
||||
clearTokents = () => {
|
||||
this.localApi.remove();
|
||||
this.sessionApi.remove();
|
||||
}
|
||||
}
|
||||
|
||||
const tokenApi = new TokenApi();
|
||||
|
||||
export default tokenApi;
|
||||
@ -34,3 +34,15 @@ export const ENDPOINTS = {
|
||||
export const TESTING_HEADERS = {
|
||||
'Api-Name': 'store-service-test',
|
||||
};
|
||||
|
||||
export const AUTH_SERVICE = 'http://api.auth.vigdorov.ru';
|
||||
|
||||
export const AUTH_ENDPOINTS = {
|
||||
AUTH: '/auth',
|
||||
REFRESH: '/refresh',
|
||||
};
|
||||
|
||||
export const LOCAL_STORAGE_TYPE = {
|
||||
LOCAL: 'LOCAL',
|
||||
SESSION: 'SESSION',
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user