为所有axios请求附加授权头

我有一个react/redux应用程序,从api服务器获取一个令牌。在用户进行身份验证之后,我希望所有axios请求都具有该令牌作为授权标头,而不必手动将其附加到操作中的每个请求。我是相当新的反应/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实例,并更新/导入该文件,而不是从node_modules中更新/导入,但当状态改变时它没有附加头文件。任何反馈/想法都非常感谢,谢谢。

382189 次浏览

对我来说,最好的解决方案是创建一个客户端服务,你将实例化你的令牌,并使用它来包装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. 你可以使用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'];
*/
}
})();

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

希望能有所帮助。

如果你想在将来调用其他api路由并将令牌保存在存储中,那么请尝试使用redux中间件

中间件可以监听api动作,并通过axios相应地分派api请求。

这里有一个非常基本的例子:

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

类似地,我们有一个函数来从调用中设置或删除令牌,如下所示:

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

我们总是在初始化时清除现有的令牌,然后建立接收到的令牌。

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

祝你好运。

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


}

尝试像下面这样创建一个新实例

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).......
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来获取存储中的令牌,则需要在添加标头之前检测路径。如果你不这样做,它也会尝试将头部添加到该调用中,并陷入循环路径问题。添加regex来检测其他调用的相反方法也可以工作
  2. 如果存储返回一个承诺,则需要返回对存储的调用,以在authHandler函数中解析承诺。Async/Await功能将使这更容易/更明显
  3. 如果对认证令牌的调用失败,或者是获取令牌的调用,则仍然需要使用配置解析承诺

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

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