我正在尝试向amazonS3预先签名的URL发送PUT请求。即使我只有一个PUT请求,我的请求似乎也会被调用两次。第一个请求返回200 OK
,第二个请求返回400 Bad Request
。
这是我的代码:
var req = {
method: 'PUT',
url: presignedUrl,
headers: {
'Content-Type': 'text/csv'
},
data: <some file in base64 format>
};
$http(req).success(function(result) {
console.log('SUCCESS!');
}).error(function(error) {
console.log('FAILED!', error);
});
400 Bad Request
更详细的错误:
<?xml version="1.0" encoding="UTF-8"?>
<Error>
<Code>InvalidArgument</Code>
<Message>Only one auth mechanism allowed; only the X-Amz-Algorithm query parameter, Signature query string parameter or the Authorization header should be specified</Message>
<ArgumentName>Authorization</ArgumentName>
<ArgumentValue>Bearer someToken</ArgumentValue>
<RequestId>someRequestId</RequestId>
<HostId>someHostId</HostId>
</Error>
我不明白的是,为什么它会回归400?什么是解决方法?
答案 0 :(得分:19)
您的客户端可能正在发送使用Authorization标头的初始请求,该标头正在使用302进行响应。响应包括具有Signature参数的Location标头。问题是初始请求中的标头被复制到后续重定向请求中,因此它包含授权和签名。如果您从后续请求中删除授权,那么您应该是好的。
这发生在我身上,但是在Java / HttpClient环境中。我可以在Java中提供解决方案的详细信息,但遗憾的是不能用于AngularJS。
答案 1 :(得分:1)
我知道这可能为时已晚,无法回答,但就像@mlohbihler所说的那样,对我来说这个错误的原因是我在Angular中设置的http拦截器发送的Authorization标头。 基本上,我没有正确地过滤掉AWS S3域,以避免它自动获取JWT授权头。
答案 2 :(得分:1)
此外,400“无效参数”可能表示由于您的S3 :: Presigner的配置/凭据错误而导致该URL开始处于预设状态。一旦超过400,您可能会遇到像我一样的501“未实现”的响应。能够通过指定Content-Length标头(指定here作为必需标头)来解决它。希望这有助于@arjuncc,它在使用预签名网址测试s3图片上传时解决了我的邮递员问题。
答案 3 :(得分:1)
对于Google员工,如果您通过Cloudfront发送已签名的(签名v4)S3请求,并且在Cloudfront Origin设置中将“限制存储桶访问”设置为“是”,则Cloudfront会将Authorization标头添加到您的请求中,您会收到此错误。不过,由于您已经签署了请求,因此应该可以关闭此设置,而不必牺牲任何安全性。
答案 4 :(得分:0)
该消息表明仅允许一次身份验证。可能是您发送的一个是带有auth参数的url,另一个是授权标头。
答案 5 :(得分:0)
import 'package:dio/adapter.dart';
import 'package:dio/dio.dart';
import 'package:scavenger_inc_flutter/utils/AuthUtils.dart';
import 'package:scavenger_inc_flutter/utils/URLS.dart';
class ApiClient {
static Dio dio;
static Dio getClient() {
if (dio == null) {
dio = new Dio();
dio.httpClientAdapter = new CustomHttpAdapter();
}
return dio;
}
}
class CustomHttpAdapter extends HttpClientAdapter {
DefaultHttpClientAdapter _adapter = DefaultHttpClientAdapter();
@override
void close({bool force = false}) {
_adapter.close(force: force);
}
@override
Future<ResponseBody> fetch(RequestOptions options,
Stream<List<int>> requestStream, Future<dynamic> cancelFuture) async {
String url = options.uri.toString();
if (url.contains(URLS.IP_ADDRESS) && await AuthUtils.isLoggedIn()) {
options.followRedirects = false;
options.headers.addAll({"Authorization": await AuthUtils.getJwtToken()});
}
final response = await _adapter.fetch(options, requestStream, cancelFuture);
if (response.statusCode == 302 || response.statusCode == 307) {
String redirect = (response.headers["location"][0]);
if(!redirect.contains(URLS.IP_ADDRESS)) {
options.path = redirect;
options.headers.clear();
}
return await fetch(options, requestStream, cancelFuture);
}
return response;
}
}
我不允许进行重定向。
使用响应对象检查它是否已重定向。
如果它是302或307(HTTP重定向代码),我在清除身份验证标头后会重新发送请求。
仅当路径包含我的特定域URL(在此示例中为IP地址)时,我才使用附加检查发送标头。
以上所有方法,都使用Dio中的CustomHttpAdapter。通过将ResponseType更改为字节,也可以用于图像。
让我知道这是否对您有帮助!
答案 6 :(得分:0)
我使用的是 django restframework。我在 REST API 中应用了令牌身份验证。我曾经在 django API 的请求头中传递令牌(使用了浏览器的 ModHeader 扩展,它自动将令牌放入请求头的授权中),直到这里一切正常。
但是在点击图像/文件(现在显示 s3 URL)时。授权自动通过。因此,问题。
链接看起来与此类似。
我锁定 ModHeader 扩展以仅在对 REST API 进行休息时而不是在对 S3 资源进行请求时传递授权令牌。即在向 S3 资源发出请求时不要传递任何其他授权。
这是一个愚蠢的错误。但万一有帮助。