问题
I keep getting a 403 when trying to upload from the client side. Is this due to not having conditions on the bucket? If I just specify the key - with no accesskey, signature, or policy - it will upload fine.
Bucket policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AddPerm",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:*",
"Resource": "arn:aws:s3:::example/*"
}
]
}
CORS (open due to being local development)
<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
<AllowedOrigin>*</AllowedOrigin>
<AllowedMethod>GET</AllowedMethod>
<AllowedMethod>PUT</AllowedMethod>
<AllowedMethod>POST</AllowedMethod>
<AllowedMethod>DELETE</AllowedMethod>
<AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>
Signature generation
///whats returned - in controller
string_to_sign
set_s3_direct_post(photo)
render :json => {
:policy => @policy,
:signature => sig,
:key => Rails.application.secrets.aws_access_key_id,
:success=>true,
:store=> photo.photo.store_dir,
:time => @time_policy,
:time_date => @date_stamp,
:form_data => @s3_direct_post
}
------------------------------------------------------------------
private
def string_to_sign
@time = Time.now.utc
@time_policy = @time.strftime('%Y%m%dT000000Z')
@date_stamp = @time.strftime('%Y%m%d')
ret = {"expiration" => 1.day.from_now.utc.xmlschema,
"conditions" => [
{"bucket" => Rails.application.secrets.aws_bucket},
{"x-amz-credential": "#{Rails.application.secrets.aws_access_key_id}/#{@date_stamp}/us-west-2/s3/aws4_request"},
{"x-amz-algorithm": "AWS4-HMAC-SHA256"},
{"x-amz-date": @time_policy },
]
}
@policy = Base64.encode64(ret.to_json).gsub(/\n/,'').gsub(/\r/,'')
end
def getSignatureKey
kDate = OpenSSL::HMAC.digest('sha256', ("AWS4" + Rails.application.secrets.aws_secret_access_key), @date_stamp)
kRegion = OpenSSL::HMAC.digest('sha256', kDate, 'us-west-2')
kService = OpenSSL::HMAC.digest('sha256', kRegion, 's3')
kSigning = OpenSSL::HMAC.digest('sha256', kService, "aws4_request")
end
def sig
sig = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), getSignatureKey, @policy).gsub(/\n|\r/, '')
end
Client:
var self=this;
$(`#song-upload`).fileupload({
url: `https://${self._backend.BUCKET}.s3.amazonaws.com`,
dataType: 'json',
add: function (e, data) {
var data_add = data;
$.ajax({
url: `${self._backend.SERVER_URL}/api/photo/new`,
data: {'authorization': `Bearer ${self._auth.isLoggedIn.getCookie('_auth')}`, post_type: 1, file_name:this.file_name},
type: 'POST',
success: function(data) {
if(data.success){
console.log(data);
self.key = data.key;
self.policy = data.policy;
self.signature = data.signature;
self.store_dir = data.store;
self.upload_time = data.time;
self.upload_date = data.time_date;
data_add.submit();
}
}
});
},
submit: function (e, data) {
data.formData = {key:`${self.store_dir}/${self.file_name}`,AWSAccessKeyId: self.key, "Policy":self.policy, "x-amz-algorithm":"AWS4-HMAC-SHA256","Signature":self.signature,"x-amz-credential":`${self.key}/${self.upload_date}/us-west-2/s3/aws4_request`, "x-amz-date":self.upload_time};
},
progress: function (e, data) {
var progress = Math.floor(((parseInt(data.loaded)*0.9) / (parseInt(data.total))) * 100);
$('#inner-progress').css({'transform':`translateX(${progress}%)`});
$('#progress-text').text(progress);
},
done: function (e, data) {
$('#inner-progress').css({'transform':`translateX(100%)`});
$('#progress-text').text(100);
if(e) console.log(e);
}
});
回答1:
If someone has this, and is trying to do a javascript upload, try plugging in the values into the html file found here. Amazon will tell you the actual errors, instead of just a 403 response.
I was missing the ["starts-with", "$key", "uploads"]
in my base64'd config.
Here's my end configurations:
Bucket Config:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Allow Get",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::example-development/*"
},
{
"Sid": "AddPerm",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789:user/example"
},
"Action": "s3:*",
"Resource": ["arn:aws:s3:::example-development/*","arn:aws:s3:::example-development"]
}
]
}
Bucket
<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
<AllowedOrigin>*</AllowedOrigin>
<AllowedMethod>GET</AllowedMethod>
<AllowedMethod>PUT</AllowedMethod>
<AllowedMethod>POST</AllowedMethod>
<AllowedMethod>DELETE</AllowedMethod>
<AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>
Backend:
string_to_sign
set_s3_direct_post(song)
render :json => {
:policy => @policy,
:signature => sig,
:key => Rails.application.secrets.aws_access_key_id,
:success=>true,
:store=> song.song.store_dir,
:time => @time_policy,
:time_date => @date_stamp,
:form_data => @s3_direct_post
}
def string_to_sign
@time = Time.now.utc
@time_policy = @time.strftime('%Y%m%dT000000Z')
@date_stamp = @time.strftime('%Y%m%d')
ret = {"expiration" => 10.hours.from_now.utc.iso8601,
"conditions" => [
{"bucket" => 'waydope-development'},
{"x-amz-credential": "#{Rails.application.secrets.aws_access_key_id}/#{@date_stamp}/us-west-2/s3/aws4_request"},
{"x-amz-algorithm": "AWS4-HMAC-SHA256"},
{"x-amz-date": @time_policy },
["starts-with", "$key", "uploads"]
]
}
@policy = Base64.encode64(ret.to_json).gsub(/\n|\r/, '')
end
def getSignatureKey
kDate = OpenSSL::HMAC.digest('sha256', ("AWS4" + Rails.application.secrets.aws_secret_access_key), @date_stamp)
kRegion = OpenSSL::HMAC.digest('sha256', kDate, 'us-west-2')
kService = OpenSSL::HMAC.digest('sha256', kRegion, 's3')
kSigning = OpenSSL::HMAC.digest('sha256', kService, "aws4_request")
end
def sig
# sig = Base64.encode64(OpenSSL::HMAC.digest('sha256', getSignatureKey, @policy)).gsub(/\n|\r/, '')
sig = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), getSignatureKey, @policy).gsub(/\n|\r/, '')
end
Client:
var self=this;
$(`#song-upload`).fileupload({
url: `https://${self._backend.BUCKET}.s3.amazonaws.com`,
dataType: 'multipart/form-data',
add: function (e, data) {
var data_add = data;
$.ajax({
url: `${self._backend.SERVER_URL}/api/music/new`,
data: {'authorization': `Bearer ${self._auth.isLoggedIn.getCookie('_waydope')}`, post_type: 1, file_name:this.file_name},
type: 'POST',
success: function(data) {
if(data.success){
console.log(data);
self.key = data.key;
self.policy = data.policy;
self.signature = data.signature;
self.store_dir = data.store;
self.upload_time = data.time;
self.upload_date = data.time_date;
data_add.submit();
}
}
});
},
submit: function (e, data) {
data.formData = {key:`${self.store_dir}/${self.file_name}`, "Policy":self.policy,"X-Amz-Signature":self.signature,"X-Amz-Credential":`${self.key}/${self.upload_date}/us-west-2/s3/aws4_request`,"X-Amz-Algorithm":"AWS4-HMAC-SHA256", "X-Amz-Date":self.upload_time, "acl": "public-read"};
},
progress: function (e, data) {
var progress = Math.floor(((parseInt(data.loaded)*0.9) / (parseInt(data.total))) * 100);
$('#inner-progress').css({'transform':`translateX(${progress}%)`});
$('#progress-text').text(progress);
},
done: function (e, data) {
$('#inner-progress').css({'transform':`translateX(100%)`});
$('#progress-text').text(100);
if(e) console.log(e);
}
});
来源:https://stackoverflow.com/questions/42592504/upload-file-to-s3-on-client-side-with-rails-carrierwave-direct-and-jquery-file