为所有axios请求附加授权标头


127

我有一个react / redux应用程序,可从api服务器获取令牌。用户验证之后,我想使所有axios请求都将该令牌作为Authorization标头,而不必手动将其附加到操作中的每个请求。我对React / 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导入),但是当状态更改时,它不会附加标头。非常感谢任何反馈/想法。


从服务器收到令牌后,您将授权令牌存储在哪里?localStorage?
Hardik Modha

在redux store session.token中
偏偏

Answers:


199

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

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了,但是会看看中间件将令牌附加到头文件中,谢谢!
21:34

1
@awwester您不需要中间件在令牌中附加令牌。在标头中附加令牌axios.defaults.header.common[Auth_Token] = token就这么简单。
Hardik Modha

4
@HardikModha我很好奇如何使用Fetch API做到这一点。
罗兰

@Rowland我相信,您需要在fetch API上编写一个包装程序以实现相同的目的。该问题的详细答案超出了OP提出的问题的范围。您可以提出另一个问题:)
Hardik Modha

2
嗨@HardikModha 如果要续订令牌时使用默认标题作为设置令牌,则无法再次将其设置为标题。这是正确的吗?所以我必须使用拦截器。
Beginnerdeveloper

48

如果使用“ 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;
  });

然后,对于任何请求,将从本地存储中选择令牌,并将其添加到请求标头中。

我在整个应用程序中使用以下代码使用同一实例:

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很高兴,重点是使用轴距的“拦截器”
llioor

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
如果要使用“应用程序类型”标头制作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应用程序运行时要执行的任何文件中,例如在routes文件中。

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函数中解析承诺。异步/等待功能将使其更容易/更明显
  3. 如果对auth令牌的调用失败或是获取令牌的调用,则您仍想使用config来解决promise
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.