Allega intestazione di autorizzazione per tutte le richieste axios


129

Ho un'applicazione React / Redux che recupera un token da un server API. Dopo che l'utente si è autenticato, vorrei fare in modo che tutte le richieste axios abbiano quel token come intestazione di autorizzazione senza doverlo allegare manualmente a ogni richiesta nell'azione. Sono abbastanza nuovo per reagire / redux e non sono sicuro dell'approccio migliore e non trovo alcun successo di qualità su Google.

Ecco la mia configurazione 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;
  }
}

Il mio token è archiviato nel redux store sotto state.session.token.

Sono un po 'perso su come procedere. Ho provato a creare un'istanza di axios in un file nella mia directory principale e ad aggiornarlo / importarlo invece che da node_modules ma non allega l'intestazione quando lo stato cambia. Qualsiasi feedback / idea è molto apprezzato, grazie.


Dove si memorizza il token di autorizzazione dopo che il token è stato ricevuto dal server? memoria locale?
Hardik Modha

in redux store session.token
awwester

Risposte:


200

Esistono diversi modi per ottenere questo risultato. Qui ho spiegato i due approcci più comuni.

1. È possibile utilizzare axios interceptor per intercettare eventuali richieste e aggiungere intestazioni di autorizzazione.

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

    return config;
});

2. Dalla documentazione di axiospuoi vedere che è disponibile un meccanismo che ti permette di impostare l'intestazione predefinita che verrà inviata con ogni richiesta che fai.

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

Quindi nel tuo caso:

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

Se lo desideri, puoi creare una funzione autoeseguibile che imposterà l'intestazione dell'autorizzazione stessa quando il token è presente nel negozio.

(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'];
         */
     }
})();

Ora non è più necessario allegare il token manualmente a ogni richiesta. È possibile inserire la funzione di cui sopra nel file che è garantito per essere eseguito ogni volta ( es: File che contiene le rotte).

Spero che sia d'aiuto :)


1
già usando redux-persist ma daremo un'occhiata al middleware per allegare il token nell'intestazione, grazie!
awwester

1
@awwester Non è necessario il middleware per allegare il token nell'intestazione. Collegare il token nell'intestazione è axios.defaults.header.common[Auth_Token] = tokencosì semplice.
Hardik Modha

4
@HardikModha Sono curioso di sapere come si potrebbe essere in grado di farlo con Fetch API.
Rowland

@Rowland credo, dovrai scrivere un wrapper sull'API di recupero per ottenere lo stesso risultato. La risposta dettagliata a tale domanda esula dall'ambito della domanda posta dal PO. Puoi fare un'altra domanda :)
Hardik Modha

2
Ciao @HardikModha. Se utilizzo le intestazioni predefinite per il token impostato quando voglio rinnovare il token, non è possibile reimpostarlo nell'intestazione. è corretto? Quindi devo usare gli intercettori.
Beginerdeveloper

50

Se usi la versione "axios": "^ 0.17.1" puoi fare così:

Crea istanza di 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;
  });

Quindi per qualsiasi richiesta il token verrà selezionato da localStorage e verrà aggiunto alle intestazioni della richiesta.

Sto usando la stessa istanza in tutta l'app con questo codice:

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

In bocca al lupo.


@ NguyễnPhúc Con piacere, il punto è usare gli "intercettatori" di axios
llioor

Questa è la migliore risposta ... per inizializzare il token sugli intercettori per ogni richiesta! . Grazie
james asso

45

La soluzione migliore per me è creare un servizio client che istanzerai con il tuo token e lo userai per avvolgere 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);

In questo client, puoi anche recuperare il token dal localStorage / cookie, come desideri.


1
E se volessi creare request.get () con intestazioni "application-type". Con il tuo approccio, le intestazioni di defaultOptions verranno sovrascritte dalle intestazioni di richiesta. Ho ragione? Grazie.
nipuro

9

Allo stesso modo, abbiamo una funzione per impostare o eliminare il token da chiamate come questa:

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}`;
  }
}

Puliamo sempre il token esistente durante l'inizializzazione, quindi stabiliamo quello ricevuto.


5

Se in futuro desideri chiamare altre route API e mantenere il tuo token nello store, prova a utilizzare il middleware redux .

Il middleware potrebbe ascoltare l'azione an api e inviare richieste api tramite axios di conseguenza.

Ecco un esempio molto semplice:

azioni / 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'
  };
}

middleware / 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

A volte si verifica un caso in cui alcune delle richieste effettuate con axios sono indirizzate a endpoint che non accettano intestazioni di autorizzazione. Pertanto, un modo alternativo per impostare l'intestazione di autorizzazione solo sul dominio consentito è come nell'esempio seguente. Posiziona la seguente funzione in qualsiasi file che viene eseguito ogni volta che viene eseguita l'applicazione React, come nel file delle rotte.

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

Prova a creare una nuova istanza come ho fatto di seguito

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

Come usarlo

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

Mi sono imbattuto in alcuni trucchi quando ho cercato di implementare qualcosa di simile e sulla base di queste risposte questo è quello che ho pensato. I problemi che stavo riscontrando erano:

  1. Se utilizzi axios per la richiesta di ottenere un token nel tuo negozio, devi rilevare il percorso prima di aggiungere l'intestazione. In caso contrario, proverà ad aggiungere anche l'intestazione a quella chiamata e ad entrare in un problema di percorso circolare. Anche l'inverso dell'aggiunta di regex per rilevare le altre chiamate funzionerebbe
  2. Se il negozio restituisce una promessa, è necessario restituire la chiamata al negozio per risolvere la promessa nella funzione authHandler. La funzionalità Async / Await lo renderebbe più facile / più ovvio
  3. Se la chiamata per il token di autenticazione fallisce o è la chiamata per ottenere il token, vuoi comunque risolvere una promessa con la configurazione

0

Il punto è impostare il token sugli intercettori per ogni richiesta

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;
});
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.