AWS S3 Bucket - getSignedUrl PUT возвращает 400 (неверный запрос)

У меня возникла странная проблема в моем проекте: мне не удалось найти, что я сделал не так. Я пытаюсь загрузить изображение в свой блог, и для этого я использую конфигурацию aws s3. Я считаю, что мои конфиги исправлены, но на всякий случай добавлю их туда:

вот моя конфигурация корса

<?xml version = "1.0" encoding = "UTF-8"?>
<CORSConfiguration xmlns = "http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
    <AllowedOrigin>*</AllowedOrigin>
    <AllowedMethod>GET</AllowedMethod>
    <MaxAgeSeconds>3000</MaxAgeSeconds>
    <AllowedHeader>Authorization</AllowedHeader>
</CORSRule>
<CORSRule>
    <AllowedOrigin>http://localhost:3000</AllowedOrigin>
    <AllowedMethod>PUT</AllowedMethod>
    <MaxAgeSeconds>3000</MaxAgeSeconds>
    <AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>

вся моя политика IAM и пользовательский api в порядке и активированы. Вот код в моем бэкэнде:

const s3 = new AWS.S3({
  accessKeyId: keys.aws.clientID,
  secretAccessKey: keys.aws.clientSecret,
  signatureVersion: "v4",
  region: "eu-west-3"
})

const router = express.Router()
// @route  GET api/posts/upload
// @desc   Upload an image on amazone server API
// @access Private
router.get(
  "/upload",
  passport.authenticate("jwt", { session: false }),
  (req, res) => {
    const key = `${req.user.id}/${uuid()}.jpeg`
    s3.getSignedUrl(
      "putObject",
      {
        Bucket: "bebeyogini",
        ContentType: "image/jpeg",
        Key: key
      },
      (err, url) => res.send({ key, url })
    )
  }
)

Когда я вызываю конечную точку / upload, она возвращает

<Error>
<Code>SignatureDoesNotMatch</Code>
<Message>
The request signature we calculated does not match the signature you provided. Check your key and signing method.
</Message>

вот почему в моем интерфейсе (в сокращении) я добавил PUT, где я добавляю файл типа содержимого в заголовки. Но все равно это не работает в этом проекте

export const sendPost = (newPost, file, history) => async dispatch => {
  dispatch(loading())
  const uploadConfig = await axios.get("/api/posts/upload")
  console.info(uploadConfig)
  console.info(uploadConfig.data)
  await axios.put(uploadConfig.data.url, file, {
    headers: {
      "Content-Type": file.type
    }
  })
  const res = await axios.post("/api/posts", {
    ...newPost,
    imageUrl: uploadConfig.data.key
  })
  history.push("/dashboard")
  dispatch({
    type: POSTS_FETCHED,
    payload: res.data
  })
}

Мне удалось несколько месяцев назад в другом проекте заставить aws s3 работать с той же конфигурацией, и единственное различие, которое у меня было между обоими, заключается в том, что тот, который работал, был настроен с сеансом cookie, а это - токен jwt в заголовке .

Если у кого-то есть идея ... Я полностью застрял! здесь репозиторий

статус ошибок:

bebeyogini.s3.eu-west-3.amazonaws.com/5bc0eca10743075fecb360f2/8bb4ff60-d8e7-11e8-ad9d-f776f418fd42.jpeg?Content-Type=image%2Fjpeg&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIAMYTIX267RJ5E4A%2F20181026%2Feu-west-3%2Fs3%2Faws4_request&X-Amz-Date=20181026T062242Z&X-Amz-Expires=900&X-Amz-Signature=943652af4bea7e8e0e6d0f97503ea997817e7d68e0540c599643d612d71fe693&X-Amz-SignedHeaders=host:1 PUT https://bebeyogini.s3.eu-west-3.amazonaws.com/5bc0eca10743075fecb360f2/8bb4ff60-d8e7-11e8-ad9d-f776f418fd42.jpeg?Content-Type=image%2Fjpeg&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIAMYTIX267RJ5E4A%2F20181026%2Feu-west-3%2Fs3%2Faws4_request&X-Amz-Date=20181026T062242Z&X-Amz-Expires=900&X-Amz-Signature=943652af4bea7e8e0e6d0f97503ea997817e7d68e0540c599643d612d71fe693&X-Amz-SignedHeaders=host 400 (Bad Request)
createError.js:17 Uncaught (in promise) Error: Request failed with status code 400
    at createError (createError.js:17)
    at settle (settle.js:19)
    at XMLHttpRequest.handleLoad (xhr.js:78)

https://github.com/erwanriou/bebeyogini

400 имеет ряд потенциальных причин, задокументированных в docs.aws.amazon.com/AmazonS3/latest/API/ErrorResponses.html

jarmod 26.10.2018 02:41

Я обновил сообщение со статусом ошибок ...

Erwan A. R. Riou 26.10.2018 08:24
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
2
2 926
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

The request signature we calculated does not match the signature you provided. Check your key and signing method. это означает, что ваши данные предварительной подписи не совпадают с данными, которые вы хотите загрузить.

Ваши подписанные данные:

{
    Key: `${req.user.id}/${uuid()}.jpeg`
    ContentType: "image/jpeg",
}

Мета вашего файла:

{
    Name: file.name
    ContentType: "image/jpeg",
}

file.name! == ${req.user.id}/${uuid()}.jpeg

Вы можете изменить имя файла на стороне клиента на то же имя файла (которое было сгенерировано) на стороне сервера или использовать противоположный способ

Понятно, это многое объяснило бы! Я постараюсь обновить это прямо сейчас!

Erwan A. R. Riou 26.10.2018 19:56

Что ж, я смотрю и просто не могу понять, как вы могли сказать, что моя мета файла - это file.name, я проверял снова и снова, и я не знаю, что вы имеете в виду под Name: file.name

Erwan A. R. Riou 28.10.2018 01:45

Функция sendPost в вашем интерфейсе, второй параметр - file. Это файловый объект, file.name - это исходное имя файла (то, что было загружено с вашего компьютера).

hoangdv 28.10.2018 03:28

Хорошо, в действии функция PUT, которую я использую в качестве первого аргумента uploadConfig.data.url, где я предполагаю загрузить данные, а затем вы говорите, что во втором аргументе, который является файлом, он автоматически обнаруживает событие file.name я не хочу, чтобы он это делал? Я попытался установить такое же имя в бэкэнде, преобразовав ключ = 'test.jpeg' и импортировав изображение с именем 'test.jpeg', чтобы оно соответствовало этому имени файла, но у меня была такая же ошибка.

Erwan A. R. Riou 28.10.2018 11:37

Дело в том, что с использованием точно такого же пользовательского интерфейса и бэкэнда, но с настройкой аутентификации с использованием файлов cookie, а не токена jwt, он отлично работает. Но я хочу jwt, потому что я не хочу делать это только на навигаторах ... Я не знаю почему, но я думаю, что заголовки авторизации делают что-то странное, и aws s3 это не нравится.

Erwan A. R. Riou 28.10.2018 11:39
Ответ принят как подходящий

Хорошо, после нескольких ночей страданий я нашел решение проблемы. Имейте в виду, что AWS S3 не принимает токен-носитель jwt, включенный в его запрос PUT. Поэтому вам нужно отключить его при выполнении запроса. Здесь я мог бы исправить это на моем фронте с помощью библиотеки axios ...

export const sendPost = (values, file, history) => async dispatch => {
  dispatch(loading())
  const uploadConfig = await axios.get("/api/posts/upload")
  delete axios.defaults.headers.common["Authorization"]
  await axios.put(uploadConfig.data.url, file, {
    headers: {
      ContentType: file.type
    }
  })
  const token = localStorage.getItem("jwtToken")
  axios.defaults.headers.common["Authorization"] = token
  const res = await axios.post("/api/posts", {
    ...values,
    imageUrl: uploadConfig.data.key
  })
  dispatch({
    type: POSTS_FETCHED,
    payload: res.data
  })
  history.push("/dashboard")
}

Это решило мои проблемы. Я использовал встроенный в Nuxt модуль $axios, который включает заголовки Authorization. Мне пришлось импортировать и создать новый экземпляр axios.

Soviut 25.07.2020 19:47

это сэкономило мне время.

Yoosam Kang 13.11.2020 08:49

это потенциально сэкономило мне часы и часы работы. Спасибо.

ninjaneer 03.09.2021 12:43

Другие вопросы по теме