Laravel保护Amazon s3桶文件



我正在使用Amazon s3,但这里我面临两个问题

1。当我提交表单时,我不能直接上传文件到亚马逊服务器。我的意思是我必须将图像上传到我的PHP服务器上的upload folder,从那里我必须检索并将它们上传到s3 server。是否有办法上传图像直接到s3时,我们点击提交?

2。如果我在s3 put object中通过'public',那么我可以访问或查看文件,但如果我将其公开,每个人都可以查看文件。但我需要保护所有的文件和视图,只有通过身份验证的用户。谁能建议我如何解决这个问题?

try {           
    $s3 = Storage::disk('s3');
    $s3->put($strFileName, file_get_contents($img_path.$strFileName), 'public');
} catch (AwsExceptionS3Exception $e) {
    echo "There was an error uploading the file.n"+$e;
}

在问问题之前,我已经从stackoverflow读了很多答案,但它没有帮助我解决我的问题。谢谢。

对于您的第一个问题,可以直接将图片上传到AWS S3。

$s3 = Storage::disk('s3')->getDriver();
$s3->put($filePath, file_get_contents($file), array('ContentDisposition' => 'attachment; filename=' . $file_name . '', 'ACL' => 'public-read'));

您应该指定您的文件路径和您从表单中获得的文件。

1。当我们点击提交

时,有没有办法直接上传图像?

是的

如何:

您需要使用JavaScript(带AJAX)分两部分完成此操作;

a)当用户单击"提交"时,捕获此事件,首先上传文件(参见http://docs.aws.amazon.com/AWSJavaScriptSDK/guide/browser-examples.html示例),并且b)然后通过AJAX提交表单并处理响应。

然而:

这允许用户上传任何东西,并可能导致问题。有提示(就在示例下面)创建15分钟(变量)的经过身份验证的url,但是如果用户花费超过15分钟,或试图在15分钟内上传100个文件,或上传图像文件以外的东西,或格式错误的图像文件等,会发生什么?

这是更安全的拉到你的服务器,并确认他们是图像和你需要的类型/大小,然后从服务器上传。

当然,如果这是一个简单的管理工具,你可以控制谁可以访问代码,那么就使用它吧——希望你只会上传你期望的内容。

2。我需要保护所有的文件,并且只查看经过认证的用户

通过"认证的用户":如果你指的是"上传图像的用户",那么s3单独不提供该功能,但CloudFront可以。您可以发布预先授权的url或签名的cookie: http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-choosing-signed-urls-cookies.html

如果"认证用户"是指上传文件的人,那么根据文档,如果没有访问底层客户端,这在Laravel类中是不可能的。默认情况下,publicprivate是您唯一的可见性选项,它们被转换为public-read,但您需要authenticated-read, bucket-owner-read或其他罐装ACL授予之一(参考:http://docs.aws.amazon.com/AmazonS3/latest/dev/acl-overview.html#canned-acl)如果authenticated-read或其他罐装ACL授予不能提供您需要的权限配置文件,您可以创建自己的权限配置文件(在同一页面上进一步了解详细信息)。

解决方案是获取底层客户端,然后直接执行put-object。(然后如果你走了那么远,你也可以放弃Laravel库并拉入s3并自己完成-然后你可以完全控制一切)。

$client = $disk->getDriver()->getAdapter()->getClient();
$client->putObject([
    'Bucket' => $bucket,
    'Key'    => $fileName,
    'Body'   => $fileBody,
    'ACL'    => 'authenticated-read'  /* Or which ACL suits */
]);

我最近解决了这个问题。首先,是的,你可以直接上传到s3这里是我使用的一些信息:http://docs.aws.amazon.com/AmazonS3/latest/dev/HTTPPOSTExamples.html

首先,您需要创建一个策略和签名服务器端,以添加到html表单中用于上传文件。

$policy = base64_encode(json_encode([
            "expiration" => "2100-01-01T00:00:00Z",
            "conditions" => [
                ["bucket"=> "bucketname"],
                ["starts-with", '$key', "foldername"],
                ["acl" => "public-read"],
                ["starts-with", '$Content-Type', "image/"],
                ["success_action_status" => '201'],
            ]
        ]));
$signature = base64_encode(hash_hmac('sha1',$policy,getenv('S3_SECRET_KEY'),true));

现在在前端,我的表单没有使用提交按钮,你可以使用提交按钮,但你需要捕获提交,并防止表单实际提交,直到上传完成。

当我们点击保存时,它生成一个md5(使用npm安装)文件名,这样文件名就不会被随机猜测,然后它使用ajax将文件上传到S3。完成此操作后,它将文件数据和返回的aws数据放入隐藏输入并提交表单。它应该看起来像这样:

<form action="/post/url" method="POST" id="form">
    <input type="text" name="other_field" />
    <input type="file" class="form-control" id="image_uploader" name="file" accept="image/*" />
    <input type="hidden" id="hidden_medias" name="medias" value="[]" />
</form>
<input type="button" value="save" id="save" />
<script>
$(document).ready(function(){
    $('#save').click(function(){
            uploadImage(function () {
                $('#form').submit();
            });
    });
});
var uploadImage = function(callback) {
    var file = $('#image_uploader')[0].files[0];
    if(file !== undefined) {
        var data = new FormData();
        var filename = md5(file.name + Math.floor(Date.now() / 1000));
        var filenamePieces = file.name.split('.');
        var extension = filenamePieces[filenamePieces.length - 1];
        data.append('acl',"public-read");
        data.append('policy',"{!! $policy !!}");
        data.append('signature',"{!! $signature !!}");
        data.append('Content-type',"image/");
        data.append('success_action_status',"201");
        data.append('AWSAccessKeyId',"{!! getenv('S3_KEY_ID') !!}");
        data.append('key',filename + '.' + extension);
        data.append('file', file);
        var fileData = {
            type: file.type,
            name: file.name,
            size: file.size
        };
        $.ajax({
            url: 'https://{bucket_name}.s3.amazonaws.com/',
            type: 'POST',
            data: data,
            processData: false,
            contentType: false,
            success: function (awsData) {
                var xmlData = new XMLSerializer().serializeToString(awsData);
                var currentImages = JSON.parse($('#hidden_medias').val());
                currentImages.push({
                    awsData: xmlData,
                    fileData: fileData
                });
                $('#hidden_medias').val(JSON.stringify(currentImages));
                callback();
            },
            error: function (errorData) {
                console.log(errorData);
            }
        });
    }
};
</script>
然后,监听提交的控制器解析来自该输入字段的JSON并创建Media实例(我创建的一个模型),并存储每个图像的awsDatafileData

然后不像这样将html图像标签指向s3文件:

<img src="https://{bucketname}.s3.amazonaws.com/filename.jpg" />

我这样做:

<img src="/medias/{id}" />

然后路由可以通过正常的auth中间件和所有你需要在Laravel中做的。最后,该路由指向一个执行以下操作的控制器:

public function getResponse($id)
{
    $media = Media::find($id);
    return (new Response('',301,['Location' => $media->info['aws']['Location']]));
}

所以它所做的只是使用301重定向并将头位置设置为实际的aws文件。由于我们在将文件上传到aws时生成md5文件名,因此每个文件名都是md5,因此人们无法在桶中随机搜索aws文件。

解决你的问题,我也在使用,但没有laravel。

1。要将任何文件直接上传到Amazon AWS S3 Bucket的特定文件夹中,您可以这样做:

<form action="upload.php" enctype="multipart/form-data">
    <input type="file" name="file" id="file" />
    <button type="submit">Upload file</button>
</form>

PHP - upload.php

包含aws php sdk

require "vendor/autoload.php";

初始化S3 Client

$credentials = new AwsCredentialsCredentials(
    '<AWS ACCESS KEY>', 
    '<AWS ACCESS SECRET>'
);
$s3Client = AwsS3S3Client::factory(
    [
        'credentials' => credentials
        'region' => 'us-east-1',
        'version' => 'latest'
    ]
);

创建文件上传实体

$uploadEntity = array(
    'Bucket' => <S3 Bucket Name>,
    'Key'    => '<Upload_Folder_If_Any>/<FileName>',
    'Body'   => fopen($_FILES['file']['tmp_name'], 'r+'),
    //'ContentDisposition' => 'attachment; filename="<FileName>"' <-- If need to allow downloading
);

上传到s3 Bucket

try {
    // $result will be AwsResult object
    $result = $s3Client->putObject($uploadEntity);  
} catch (AwsS3ExceptionS3Exception $exception) {
    // S3 Exception
}

2。现在将上传的文件仅提供给经过身份验证的用户

首先,您需要为s3 bucket创建私有bucket策略。

Bucket Policy—可以使用Policy Generator生成桶策略。使用它,您可以生成如下的策略。抄袭自《改进》。dk网站

{
  "Id": "Policy1319566876317",
  "Statement": [
    {
      "Sid": "Stmt1319566860498",
      "Action": [
        "s3:GetObject"
      ],
      "Effect": "Allow",
      "Resource": "arn:aws:s3:::<BucketName>/*",
      "Principal": {
        "CanonicalUser": [
          "<CannoicalUserId_of_OAI>"
        ]
      }
    }
  ]
}

其次,你需要设置cloudfront s3 Bucket的私有web分发。只有这样,您才能通过aws签名url或签名cookie

向经过身份验证的用户提供您的内容。第三,要生成签名url,您需要pem文件,该文件只能从aws控制台获得。

生成签名url

$cdnName = '<AWS CLOUDFRONT WEB DISTRIBUTION CDN>';
$assetName = '<UPLOAD_FOLDER_IF_ANY>/<FILENAME>';
$expiry = time() + 300;     // 5 mins expiry time, ie. the signed url will be valid only for 5 mins
$cloudfront = CloudFrontClient::factory(array(
    'credentials' => $credentials,
    'region' => 'us-east-1',
    'version' => 'latest'
));
// Use this for creating signed url
$signedUrl = $cloudFront->getSignedUrl([
    'url'         => $cdnName . '/' . $assetName,
    'expires'     => $expiry,
    'private_key' => '/path/to/your/cloudfront-private-key.pem',
    'key_pair_id' => '<cloudfront key pair id>'
]);
// Use this for signed cookie    
$signedCookie = $cloudFront->getSignedCookie([
    'url'         => $cdnName . '/' . $assetName,
    'expires'     => $expiry,
    'private_key' => '/path/to/your/cloudfront-private-key.pem',
    'key_pair_id' => '<cloudfront key pair id>'
]);

最新更新