从Meteor Mobile Application

时间:2016-09-28 20:43:56

标签: javascript amazon-web-services meteor amazon-s3 amazon-cognito

我有一个Meteor Mobile应用程序可以访问存储在我的S3存储桶中的大量照片。这些照片是用户上传的并经常更改。我不希望任何没有使用我的应用程序的人访问这些照片。 (即:这些照片只能从我的应用程序中查看,直接在浏览器中访问网址不会加载它们。)

实现这一目标的最佳方法是什么? AWS Cognito似乎是合乎逻辑的选择,但它似乎并不容易实现,而且我不确定如何在获得Cognito身份后从客户端向AWS进行身份验证。

我的另一个想法是在每个网址上放置一个只读的AWS Key并对此进行身份验证,但这几乎毫无意义。找出钥匙和秘密真的很容易。

修改

具体来说,图像的URL位于Mongo集合中,我将它们传递给模板。因此,S3资源只是加载了图像标记(<img src=")。像AWS STS这样的东西听起来像是一个很好的选择,但我不知道在我加载它们时如何在标题中传递标记。将它们作为预先签名的查询字符串进行效率似乎效率低下。

另一个选项是使用referrer标题like this issue限制访问。但就像Martijn所说的那样,这并不是一种安全的方式。

1 个答案:

答案 0 :(得分:0)

经过一些研究和测试后,我自己解决了这个问题。我的最终解决方案是使用referer标头来限制对我的S3存储桶的访问。我创建了一个更安全,更详细的解决方案(见下文),但它的性能提升对我的应用程序无效。我的应用程序是基于查看照片和视频,而不能立即加载它们不在卡片中。虽然,我觉得它可以成为大多数用例的充分解决方案。由于我的应用不是高度敏感的,referer标题对我来说已经足够了。 Here is how to use the http header referer to limit access to a bucket.

使用亚马逊STS的解决方案:

首先,您需要在serverclient.上安装AWS SDK.Meteor没有最新的软件包,所以我创建了自己的软件包。 (我会尽快发布它,并在我这样做之后放一个链接。)

在服务器上,您必须使用能够承担角色的凭据。要假定的角色必须与承担角色的用户具有信任关系。 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;
       }
      }
     }
    }
  });

然后,您可以手动或setInterval Meteor.startup调用此方法。

  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;
 }

});

这就是它的全部!我确信这可以更好地改进,但这正是我在测试它时非常快的方法。就像我说的,它应该适用于大多数用例。我特别没有。出于某种原因,当您尝试在visibility: visible|hidden;这些signedURL上切换img src时,加载时间比直接设置网址需要更长的时间。这必须是因为亚马逊必须在返回对象之前解密签名的URL。

感谢Mikkel指示。