すべてのaxiosリクエストにAuthorizationヘッダーを添付


128

APIサーバーからトークンを取得するreact / reduxアプリケーションがあります。ユーザーが認証された後、すべてのaxiosリクエストがそのトークンをAuthorizationヘッダーとして持つようにし、アクション内のすべてのリクエストに手動でトークンを添付する必要がないようにします。私は反応/ reduxをかなり始めたばかりで、最善のアプローチについて確信が持てず、Googleで質の高いヒットを見つけていません。

これが私のreduxセットアップです:

// 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からではなくそれを更新/インポートしようとしましたが、状態が変化したときにヘッダーが添付されません。フィードバック/アイデアは大歓迎です。


トークンがサーバーから受信された後、どこに認証トークンを保存しますか?ローカルストレージ?
Hardik Modha 2017年

reduxストアsession.token
awwester

回答:


199

これを実現する方法はいくつかあります。ここでは、最も一般的な2つのアプローチについて説明しました。

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 2017年

1
@awwesterトークンをヘッダーに添付するためにミドルウェアは必要ありません。ヘッダーにトークンを添付するのはそれとaxios.defaults.header.common[Auth_Token] = token同じくらい簡単です。
Hardik Modha 2017年

4
@HardikModha Fetch APIを使用してこれを行う方法を知りたいです。
ローランド

@Rowland私は、同じことを達成するためにフェッチAPIのラッパーを書く必要があると思います。その質問に対する詳細な回答は、OPが尋ねる質問の範囲外です。あなたは別の質問をすることができます:)
Hardik Modha

2
こんにちは@HardikModha。トークンを更新するときに、設定したトークンにデフォルトのヘッダーを使用すると、ヘッダーに再度設定できません。それが正しいか?だから私はインターセプターを使用する必要があります。
beginerdeveloper

50

"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();

幸運を。


@NguyễnPhúc喜んで、全体のポイントは
axiosの

これが最良の答えです...リクエストごとにインターセプターのトークンを初期化する!。ありがとう
ジェームスエース

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からトークンを取得することもできます。


1
「application-type」ヘッダーを使用して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}`;
  }
}

初期化時に常に既存のトークンを消去してから、受信したトークンを確立します。


5

今後、他のAPIルートを呼び出してストアにトークンを保持したい場合は、reduxミドルウェアを使用してください。

ミドルウェアはapiアクションをリッスンし、それに応じてaxiosを介してapiリクエストをディスパッチできます。

これは非常に基本的な例です:

actions / 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);
};

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. ストアがpromiseを返している場合は、authHandler関数でpromiseを解決するためにストアへの呼び出しを返す必要があります。非同期/待機機能は、これをより簡単/より明白にします
  3. 認証トークンの呼び出しが失敗するか、トークンを取得するための呼び出しである場合でも、構成を使用してpromiseを解決する必要があります

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;
});
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.