【问题标题】:Attach Authorization header for all axios requests为所有 axios 请求附加授权标头
【发布时间】:2017-08-20 10:46:32
【问题描述】:

我有一个从 api 服务器获取令牌的 react/redux 应用程序。在用户进行身份验证后,我想让所有 axios 请求都将该令牌作为 Authorization 标头,而不必手动将其附加到操作中的每个请求。我对 react/redux 还很陌生,不确定最佳方法,也没有在谷歌上找到任何高质量的结果。

这是我的 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;
  }
}

我的令牌存储在 state.session.token 下的 redux 存储中。

我有点不知道如何继续。我尝试在我的根目录中的文件中创建 axios instance 并更新/导入它而不是从 node_modules 但是当状态更改时它没有附加标题。非常感谢任何反馈/想法,谢谢。

【问题讨论】:

  • 从服务器接收到授权令牌后,您将授权令牌存储在哪里?本地存储?
  • 在 redux 存储 session.token 中

标签: reactjs redux axios


【解决方案1】:

有多种方法可以实现这一目标。在这里,我解释了两种最常见的方法。

1。您可以使用axios interceptors 拦截任何请求并添加授权标头。

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

    return config;
});

2。从documentationaxios 中,您可以看到有一种可用的机制允许您设置默认标头,该标头将随您发出的每个请求一起发送。

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

现在您不再需要手动将令牌附加到每个请求。您可以将上述函数放在保证每次都执行的文件中(例如:包含路由的文件)。

希望对你有帮助:)

【讨论】:

  • 已经在使用 redux-persist 但会看看中间件以在标头中附加令牌,谢谢!
  • @awwester 您不需要中间件将令牌附加到标头中。在标头中附加令牌是axios.defaults.header.common[Auth_Token] = token,就这么简单。
  • @HardikModha 我很好奇如何使用 Fetch API 做到这一点。
  • 嗨@HardikModha。如果我想更新令牌时使用设置令牌的默认标头,则无法再次将其设置到标头中。这是正确的吗?所以我必须使用拦截器。
  • 一个小问题:如果您遵循第二种方法,您将必须为应用程序中的每个 Axios 实例分别设置默认标头。我花了一段时间才弄清楚。
【解决方案2】:

创建 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 的“拦截器”
  • 这是最好的答案...为每个请求初始化拦截器上的令牌! .谢谢
【解决方案3】:

对我来说最好的解决方案是创建一个客户端服务,您将使用令牌实例化它并使用它来包装 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 中检索令牌。

【讨论】:

  • 如果您想使用“应用程序类型”标头制作 request.get() 怎么办。使用您的方法,来自 defaultOptions 的标头将被请求的标头覆盖。我是对的?谢谢。
【解决方案4】:

同样,我们有一个函数可以设置或删除调用中的令牌,如下所示:

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】:

    重点是为每个请求在拦截器上设置令牌

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

    【讨论】:

      【解决方案6】:

      如果您以后想调用其他 api 路由并将您的令牌保存在商店中,请尝试using redux middleware

      中间件可以监听一个 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);
      };
      

      【讨论】:

        【解决方案7】:

        有时您会遇到这样一种情况,即使用 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);
            });
        
        }
        

        【讨论】:

          【解决方案8】:

          尝试像下面那样创建新实例

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

          【讨论】:

            【解决方案9】:
            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. 如果 store 正在返回一个 Promise,您需要返回对 store 的调用以在 authHandler 函数中解析该 Promise。异步/等待功能将使这更容易/更明显
            3. 如果对 auth 令牌的调用失败或者是获取令牌的调用,您仍然希望使用配置解决一个 Promise

            【讨论】:

            • 我遇到了完全相同的问题,很高兴我找到了你的答案。有道理。
            猜你喜欢
            • 2021-03-24
            • 1970-01-01
            • 1970-01-01
            • 2020-10-11
            • 2020-01-19
            • 2021-09-06
            • 2019-11-11
            • 2015-03-15
            • 1970-01-01
            相关资源
            最近更新 更多