Прикрепите заголовок авторизации для всех запросов axios

131

У меня есть приложение response / redux, которое получает токен с сервера api. После аутентификации пользователя я хотел бы, чтобы все запросы Axios содержали этот токен в качестве заголовка авторизации без необходимости вручную прикреплять его к каждому запросу в действии. Я новичок в реакции / сокращении, и я не уверен в лучшем подходе, и я не нахожу качественных результатов в Google.

Вот моя настройка редукции:

// actions.js
import axios from 'axios';

export function loginUser(props) {
  const url = `https://api.mydomain.com/login/`;
  const { email, password } = props;
  const request = axios.post(url, { email, password });

  return {
    type: LOGIN_USER,
    payload: request
  };
}

export function fetchPages() {
  /* here is where I'd like the header to be attached automatically if the user
     has logged in */ 
  const request = axios.get(PAGES_URL);

  return {
    type: FETCH_PAGES,
    payload: request
  };
}

// reducers.js
const initialState = {
  isAuthenticated: false,
  token: null
};

export default (state = initialState, action) => {
  switch(action.type) {
    case LOGIN_USER:
      // here is where I believe I should be attaching the header to all axios requests.
      return {
        token: action.payload.data.key,
        isAuthenticated: true
      };
    case LOGOUT_USER:
      // i would remove the header from all axios requests here.
      return initialState;
    default:
      return state;
  }
}

Мой токен хранится в хранилище redux под state.session.token.

Я немного не понимаю, как действовать дальше. Я попытался создать экземпляр axios в файле в моем корневом каталоге и обновить / импортировать его вместо node_modules, но он не прикрепляет заголовок при изменении состояния. Любые отзывы / идеи приветствуются, спасибо.

awwester
источник
Где вы храните токен авторизации после получения токена с сервера? LocalStorage?
Hardik Modha
в redux store session.token
awwester

Ответы:

205

Есть несколько способов добиться этого. Здесь я объяснил два наиболее распространенных подхода.

1. Вы можете использовать перехватчики axios для перехвата любых запросов и добавления заголовков авторизации.

// Add a request interceptor
axios.interceptors.request.use(function (config) {
    const token = store.getState().session.token;
    config.headers.Authorization =  token;

    return config;
});

2. Из документации по axiosвы можете увидеть , есть доступный механизм , который позволяет установить заголовок по умолчанию , который будет отправлен с каждым запросом вы делаете.

axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;

Итак, в вашем случае:

axios.defaults.headers.common['Authorization'] = store.getState().session.token;

При желании вы можете создать самоисполняемую функцию, которая сама установит заголовок авторизации, когда токен присутствует в магазине.

(function() {
     String token = store.getState().session.token;
     if (token) {
         axios.defaults.headers.common['Authorization'] = token;
     } else {
         axios.defaults.headers.common['Authorization'] = null;
         /*if setting null does not remove `Authorization` header then try     
           delete axios.defaults.headers.common['Authorization'];
         */
     }
})();

Теперь вам больше не нужно вручную прикреплять токен к каждому запросу. Вы можете поместить указанную выше функцию в файл, который гарантированно будет выполняться каждый раз ( например: Файл, содержащий маршруты).

Надеюсь, поможет :)

Хардик Модха
источник
1
уже использует redux-persist, но мы рассмотрим промежуточное ПО, чтобы прикрепить токен в заголовок, спасибо!
awwester
1
@awwester Вам не нужно промежуточное ПО для прикрепления токена в заголовке. Прикрепить токен в заголовок очень axios.defaults.header.common[Auth_Token] = tokenпросто.
Hardik Modha
4
@HardikModha Мне любопытно, как это можно сделать с помощью Fetch API.
Rowland
@Rowland Я считаю, вам нужно написать оболочку для API выборки, чтобы добиться того же. Подробный ответ на этот вопрос выходит за рамки вопроса, заданного ОП. Вы можете задать другой вопрос :)
Hardik Modha
2
Привет @HardikModha. Если я использую заголовки по умолчанию для установленного токена, когда я хочу обновить токен, его нельзя будет снова установить в заголовке. это правильно? Поэтому мне нужно использовать перехватчики.
beginerdeveloper
51

Если вы используете версию "axios": "^ 0.17.1", вы можете сделать это так:

Создайте экземпляр axios:

// Default config options
  const defaultOptions = {
    baseURL: <CHANGE-TO-URL>,
    headers: {
      'Content-Type': 'application/json',
    },
  };

  // Create instance
  let instance = axios.create(defaultOptions);

  // Set the AUTH token for any request
  instance.interceptors.request.use(function (config) {
    const token = localStorage.getItem('token');
    config.headers.Authorization =  token ? `Bearer ${token}` : '';
    return config;
  });

Затем для любого запроса токен будет выбран из localStorage и добавлен в заголовки запроса.

Я использую один и тот же экземпляр во всем приложении с этим кодом:

import axios from 'axios';

const fetchClient = () => {
  const defaultOptions = {
    baseURL: process.env.REACT_APP_API_PATH,
    method: 'get',
    headers: {
      'Content-Type': 'application/json',
    },
  };

  // Create instance
  let instance = axios.create(defaultOptions);

  // Set the AUTH token for any request
  instance.interceptors.request.use(function (config) {
    const token = localStorage.getItem('token');
    config.headers.Authorization =  token ? `Bearer ${token}` : '';
    return config;
  });

  return instance;
};

export default fetchClient();

Удачи.

llioor
источник
@ NguyễnPhúc С удовольствием, весь смысл в использовании «перехватчиков»
аксиомов
1
Это лучший ответ ... инициализировать токен на перехватчиках для каждого запроса! . Спасибо
Джеймс Эйс
45

Лучшее решение для меня - создать клиентскую службу, которую вы будете создавать с помощью своего токена и использовать для обертывания axios.

import axios from 'axios';

const client = (token = null) => {
    const defaultOptions = {
        headers: {
            Authorization: token ? `Token ${token}` : '',
        },
    };

    return {
        get: (url, options = {}) => axios.get(url, { ...defaultOptions, ...options }),
        post: (url, data, options = {}) => axios.post(url, data, { ...defaultOptions, ...options }),
        put: (url, data, options = {}) => axios.put(url, data, { ...defaultOptions, ...options }),
        delete: (url, options = {}) => axios.delete(url, { ...defaultOptions, ...options }),
    };
};

const request = client('MY SECRET TOKEN');

request.get(PAGES_URL);

В этом клиенте вы также можете получить токен из localStorage / cookie по своему усмотрению.

Kmaschta
источник
1
Что делать, если вы хотите создать request.get () с заголовками «типа приложения». При вашем подходе заголовки из defaultOptions будут перекрываться заголовками из запроса. Я прав? Спасибо.
nipuro
9

Точно так же у нас есть функция для установки или удаления токена из таких вызовов:

import axios from 'axios';

export default function setAuthToken(token) {
  axios.defaults.headers.common['Authorization'] = '';
  delete axios.defaults.headers.common['Authorization'];

  if (token) {
    axios.defaults.headers.common['Authorization'] = `${token}`;
  }
}

Мы всегда очищаем существующий токен при инициализации, а затем устанавливаем полученный.

elQueFaltaba
источник
5

Если вы хотите в будущем вызывать другие маршруты api и хранить свой токен в магазине, попробуйте использовать промежуточное программное обеспечение redux .

Промежуточное ПО может прослушивать действие api и соответственно отправлять запросы api через axios.

Вот очень простой пример:

действия / api.js

export const CALL_API = 'CALL_API';

function onSuccess(payload) {
  return {
    type: 'SUCCESS',
    payload
  };
}

function onError(payload) {
  return {
    type: 'ERROR',
    payload,
    error: true
  };
}

export function apiLogin(credentials) {
  return {
    onSuccess,
    onError,
    type: CALL_API,
    params: { ...credentials },
    method: 'post',
    url: 'login'
  };
}

промежуточное ПО / api.js

import axios from 'axios';
import { CALL_API } from '../actions/api';

export default ({ getState, dispatch }) => next => async action => {
  // Ignore anything that's not calling the api
  if (action.type !== CALL_API) {
    return next(action);
  }

  // Grab the token from state
  const { token } = getState().session;

  // Format the request and attach the token.
  const { method, onSuccess, onError, params, url } = action;

  const defaultOptions = {
    headers: {
      Authorization: token ? `Token ${token}` : '',
    }
  };

  const options = {
    ...defaultOptions,
    ...params
  };

  try {
    const response = await axios[method](url, options);
    dispatch(onSuccess(response.data));
  } catch (error) {
    dispatch(onError(error.data));
  }

  return next(action);
};
Павел. B
источник
3

Иногда возникает ситуация, когда некоторые запросы, сделанные с помощью axios, указываются на конечные точки, которые не принимают заголовки авторизации. Таким образом, альтернативный способ установки заголовка авторизации только для разрешенного домена показан в примере ниже. Поместите следующую функцию в любой файл, который запускается каждый раз при запуске приложения React, например, в файле маршрутов.

export default () => {
    axios.interceptors.request.use(function (requestConfig) {
        if (requestConfig.url.indexOf(<ALLOWED_DOMAIN>) > -1) {
            const token = localStorage.token;
            requestConfig.headers['Authorization'] = `Bearer ${token}`;
        }

        return requestConfig;
    }, function (error) {
        return Promise.reject(error);
    });

}
Каролис Раманаускас
источник
0

Попробуйте создать новый экземпляр, как я сделал ниже

var common_axios = axios.create({
    baseURL: 'https://sample.com'
});

// Set default headers to common_axios ( as Instance )
common_axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
// Check your Header
console.log(common_axios.defaults.headers);

Как это использовать

common_axios.get(url).......
common_axios.post(url).......
угали мягкий
источник
0
export const authHandler = (config) => {
  const authRegex = /^\/apiregex/;

  if (!authRegex.test(config.url)) {
    return store.fetchToken().then((token) => {
      Object.assign(config.headers.common, { Authorization: `Bearer ${token}` });
      return Promise.resolve(config);
    });
  }
  return Promise.resolve(config);
};

axios.interceptors.request.use(authHandler);

Попал в некоторые ошибки при попытке реализовать что-то подобное, и на основе этих ответов я пришел к этому. У меня возникли следующие проблемы:

  1. Если вы используете axios для запроса на получение токена в вашем магазине, вам необходимо определить путь перед добавлением заголовка. Если вы этого не сделаете, он также попытается добавить заголовок к этому вызову и столкнется с проблемой кругового пути. Обратное добавление регулярного выражения для обнаружения других вызовов также будет работать
  2. Если магазин возвращает обещание, вам необходимо вернуть вызов в магазин, чтобы разрешить обещание в функции authHandler. Функциональность Async / Await сделает это проще / очевиднее
  3. Если вызов токена аутентификации завершается неудачно или является вызовом для получения токена, вы все равно хотите разрешить обещание с конфигурацией
Bhetzie
источник
0

Дело в том, чтобы установить токен на перехватчиках для каждого запроса.

import axios from "axios";
    
const httpClient = axios.create({
    baseURL: "http://youradress",
    // baseURL: process.env.APP_API_BASE_URL,
});

httpClient.interceptors.request.use(function (config) {
    const token = localStorage.getItem('token');
    config.headers.Authorization =  token ? `Bearer ${token}` : '';
    return config;
});
Джеймс Эйс
источник