首页 > 解决方案 > 我的 JWT 身份验证方法不允许 Cloudinary 上传

问题描述

我主要完成了 Brad Traversy 的 Udemy 教程,名为“MERN Stack Front To Back”,并遇到了几个类似的错误,试图通过让用户选择使用 Cloudinary 上传照片来扩展他的应用程序。这两个错误都与 CORS 请求有关。

在进入它之前,了解这个应用程序可能很重要,它使用npm Concurrently来运行 Node 服务器,然后使用相同的 npm run dev 命令运行 React/Redux 客户端——并且不使用中间件来处理 CORS 请求。所以我的第一个问题是,这个设置如何解决对中间件的需求?在我看来,他们仍在运行单独的服务器......

不管为什么 Brad Traversy 的应用程序没有这样的中间件,当我向应用程序的登录页面添加我自己的新操作时,该操作以与其他组件相同的方式向 api 发出请求,例如:

componentDidMount() {
  this.props.getAllAlerts();
}

export function getAllAlerts() {
  const request = axios.get(`${ROOT_URL}/api/alert`);
  return {
    type: GET_ALL_ALERTS,
    payload: request
  };
}

我收到以下错误:“加载http://localhost:5000/api/alert失败:对预检请求的响应未通过访问控制检查:请求中不存在“Access-Control-Allow-Origin”标头资源。因此,不允许访问源“ http://localhost:3000 ”。”

实际上,我只是通过添加npm cors中间件并在我的 api 服务器中使用它来完全解决了这个错误。

app.use(cors());

不过,我想知道为什么它首先发生在其他组件不需要它的情况下对 api 进行 axios 请求时 - 因为它可能有助于理解为什么稍后在添加组件时会遇到非常相似的错误将照片从浏览器直接上传到 Cloudinary。这是动作:

export const uploadFile = event => {
  const file = event.target.files[0];
  const CLOUDINARY_URL = `https://api.cloudinary.com/v1_1/${myCloudinaryApi}/upload`;
  const CLOUDINARY_UPLOAD_PRESET = CLOUDINARY_UPLOAD_PRESET;
  const formData = new FormData();
  formData.append("file", file);
  formData.append("upload_preset", CLOUDINARY_UPLOAD_PRESET);
  return dispatch => {
    return axios({
      url: CLOUDINARY_URL,
      method: "POST",
      skipAuthorization: true,
      headers: {
        "Content-Type": "application/x-www-form-urlencoded"
      },
      data: formData
    })
      .then(response => {
        dispatch({
          type: UPLOAD_FILE,
          payload: response.data.secure_url
        });
      })
      .catch(function(err) {
        console.log(err);
      });
  };
};

这是错误:“无法加载https://api.cloudinary.com/v1_1/alertsapi/upload:在预检响应中 Access-Control-Allow-Headers 不允许请求标头字段授权。”

尽管使用了 cors 中间件,但我不明白为什么我会得到这个。

最后,一个看起来相关的附加问题:这个应用程序每次加载顶级组件时都会检查 JWT 令牌:

// Check for token
if (localStorage.jwtToken) {
  // Set auth token header auth
  setAuthToken(localStorage.jwtToken);
  // Decode token and get user info and exp
  const decoded = jwt_decode(localStorage.jwtToken);
  // Set user and isAuthenticated
  store.dispatch(setCurrentUser(decoded));

  // Check for expired token
  const currentTime = Date.now() / 1000;
  if (decoded.exp < currentTime) {
    // Logout user
    store.dispatch(logoutUser());
    // Clear current Profile
    store.dispatch(clearCurrentProfile());
    // Redirect to login
    window.location.href = "/login";
  }
}

class App extends Component {
  render() {
    return ( 
...

如果我删除此检查,则 uploadFile 操作可以正常工作。因此,如果没有其他方法可以解决问题,有没有办法绕过这个仅针对 Cloudinary 上传的检查?

提前感谢任何人的帮助。让我知道我是否可以提供有关该应用程序的任何其他信息,这可能会有所帮助。

标签: node.jsreactjscorsjwtcloudinary

解决方案


我想出了第二个问题的解决方案。事实证明,uploadFile操作中的 axios 请求包含一个Authorization标头,该标头由 function 在我的身份验证检查(如上所示)中设置setAuthToken(localStorage.jwtToken)。这就是导致上述第二个错误的原因。不幸的是,该函数是从另一个文件中导入的,并没有引起我的注意。这里是:

const setAuthToken = (token`enter code here`) => {
  if (token) {
    // Apply to every request
    axios.defaults.headers.common["Authorization"] = token;
  } else {
    // Delete auth header
    delete axios.defaults.headers.common["Authorization"];
  }
};

Cloudinary 请求不允许此标头。要删除我添加的不需要的标题

delete axios.defaults.headers.common["Authorization"]

在uploadFile操作中的return dispatch => {行之后。这使得文件上传成功,但也意味着如果另一个 axios 请求直接跟随这个动作,它不会有 Authorization 头。在这种情况下,下一个操作确实包含需要 Authorization 标头的 axios 请求,因此有必要在该操作之前手动将其添加回请求之前:

axios.defaults.headers.common["Authorization"] = localStorage.jwtToken

第二个问题解决了。我仍然不清楚为什么将我自己的请求添加到同一个 api 会导致 CORS 错误。


推荐阅读