經過一番研究和測試,我自己解決了這個問題。我的最終解決方案是使用referer
標頭來限制對我的S3存儲桶的訪問。我創建了一個更安全,更詳細的解決方案(請參閱下文),但它帶來的性能下降對我的應用程序無效。我的應用基於查看照片和視頻,無法立即將它們加載到附近。雖然,我覺得這對大多數使用情況來說可能是一個充分的解決方案。因爲我的應用程序不是非常敏感,所以referer
標題對我來說已經足夠了。 Here is how to use the http header referer
to limit access to a bucket.
解決方案使用Amazon的STS:
首先,你需要有AWS SDK上server和client.雙方有沒有最新的軟件包提供流星,所以我創造了我自己。 (我將很快發佈,並在這裏把一個鏈接,一旦我做的。)
在服務器上,您必須使用必須承擔一個角色的能力憑據。要承擔的角色必須與承擔角色的用戶建立信任關係。 Article on using IAM. - Article on using credentials with SDK
在server.js
文件中,我創建了一個Meteor方法,我可以從客戶端調用。它首先檢查用戶是否登錄。如果是這樣,它會檢查當前的臨時憑證是否將在接下來的5分鐘內過期。如果是,我發出新的憑據並將它們寫入用戶文檔或將它們作爲回調函數返回。如果他們在接下來的5分鐘內沒有過期,我將返回他們當前的臨時憑證。
您必須使用Meteor.bindEnvironment
作爲回調。See docs
Meteor.methods({
'awsKey': function(){
if (Meteor.userId()){
var user = Meteor.userId();
var now = moment(new Date());
var userDoc = Meteor.users.findOne({_id: user});
var expire = moment(userDoc.aws.expiration);
var fiveMinutes = 5 * 60 * 1000;
var fut = new Future();
if(moment.duration(expire.diff(now))._milliseconds < fiveMinutes){
var params = {
RoleArn: 'arn:aws:iam::556754141176:role/RoleToAssume',
RoleSessionName: 'SessionName',
DurationSeconds: 3600 //1 Hour
};
var sts = new AWS.STS();
sts.assumeRole(params, Meteor.bindEnvironment((err, data) => {
if (err){
fut.throw(new Error(err));
}else{
Meteor.users.update({_id: user}, {$set: {aws: {accessKey: data.Credentials.AccessKeyId, secretKey: data.Credentials.SecretAccessKey, sessionToken: data.Credentials.SessionToken, expiration: data.Credentials.Expiration}}});
fut.return(data.Credentials);
}
}));
return fut.wait();
}else{
return userDoc.aws;
}
}
}
}
});
然後可以手動或Meteor.startup
在setInterval
調用此方法。
Meteor.setInterval(function(){
if(Meteor.userId()){
Meteor.call('awsKey', function(err, data){
if (err){
console.log(err);
}else{
if(data.accessKey){
Session.set('accessKey', data.accessKey);
Session.set('secretKey', data.secretKey);
Session.set('sessionToken', data.sessionToken);
}else{
Session.set('accessKey', data.AccessKeyId);
Session.set('secretKey', data.SecretAccessKey);
Session.set('sessionToken', data.SessionToken);
}
}
});
}
}, 300000); //5 Minute interval
這種方式只是從回調中設置Session變量中的鍵。你可以通過查詢用戶的文檔來獲得它們來做到這一點。
然後,您可以使用這些臨時憑據來獲取您試圖在存儲桶中訪問的對象的簽名URL。
我把這個模板助手在模板傳遞對象的名字吧:
{{getAwsUrl imageName}}
Template.templateName.helpers({
'getAwsUrl': function(filename){
var accessKey = Session.get('accessKey');
var secretKey = Session.get('secretKey');
var sessionToken = Session.get('sessionToken');
var filename = filename;
var params = {Bucket: 'bucketName', Key: filename, Expires: 6000};
new AWS.S3({accessKeyId: accessKey, secretAccessKey: secretKey, sessionToken: sessionToken, region: 'us-west-2'}).getSignedUrl('getObject', params, function (err, url) {
if (err) {
console.log("Error:" +err);
}else{
result = url;
}
});
return result;
}
});
這一切就是這麼簡單!我確信這可以被改進得更好,但這正是我在測試速度非常快時想到的。就像我說的那樣,它應該在大多數使用情況下工作。我特別的沒有。出於某種原因,當您嘗試在這些signedURL的img src
上切換visibility: visible|hidden;
時,它們將花費比加載URL更長的時間,而不僅僅是直接設置URL。這必須是因爲亞馬遜必須在返回對象之前解密簽名的URL。
感謝Mikkel的方向。
在您的應用程序中,S3圖像訪問是否已通過身份驗證?如果是,您可以授予訪問該已驗證的GET的權限,並拒絕任何公開訪問您的存儲桶 – Mikkel
不,現在他們只是在開發過程中公開可用的圖像,但我不想在生產中使用它。我不知道從客戶端進行身份驗證的方法,除了在URL或標頭中傳遞密鑰和祕密之外,兩者都可以看到。我想傳入標題會比URL更好,但不是一個可靠的解決方案。 – gkrizek
不,你絕對不想傳遞密鑰,但是你可以使用Meteor服務器發送一個http認證請求(這在瀏覽器中是不可見的),以獲得你可以傳遞的認證令牌(我認爲)在圖片GET請求 – Mikkel