Создание подписанного S3 URL с помощью Javascript - PullRequest
6 голосов
/ 15 ноября 2011

Я пытаюсь создать подписанный URL-адрес S3, используя Javascript & NodeJS. Я использовал это спецификация.

var crypto     = require('crypto'),
    date       = 1331290899,
    resource   = '/myfile.txt',
    awskey     = "XXXX",
    awssecret  = "XXXX";

var stringToSign ='GET\n\n\n' + date + '\n\n' + resource;

var sig = encodeURIComponent(crypto.createHmac('sha1', awssecret).update(stringToSign ).digest('base64'));

var url = "https://s3-eu-west-1.amazonaws.com/mybucket" +
       resource + "?AWSAccessKeyId=" + awskey + "&Expires="+ date +  
      "&Signature="+ sig

Это создает URL, похожий на этот:

https://s3-eu-west-1.amazonaws.com/mybucket/test.txt?AWSAccessKeyId=XXXXXX&Expires=1331290899&Signature=EciGxdQ1uOqgFDCRon4vPqTiCLc%3D

Однако при доступе к нему я получаю следующую ошибку:

SignatureDoesNotMatch

The request signature we calculated does not match the signature you provided. 
Check your key and signing method.

Что я делаю не так при создании подписи?

РЕДАКТИРОВАТЬ - ПОПЫТКА С НОКСОМ

Я сейчас пытаюсь использовать Knox для создания подписанного URL. Мне нужно добавить заголовки с запросом на принудительную загрузку. Я отредактировал следующее:

Добавлено amazonHeaders: 'response-content-disposition:attachment', в client.signedUrl- http://jsfiddle.net/BpGNM/1/

Добавлено options.amazonHeaders + '\n' + в auth.queryStringToSign - http://jsfiddle.net/6b8Tm/

Сообщение, которое сейчас отправляется на auth.hmacSha1 для создания подписи:

'GET\n\n\n1321374212\nresponse-content-disposition:attachment\n/meshmesh-dev/test/Readme.md'

Затем я попытался получить доступ к своему новому URL с добавлением response-content-disposition=attachment как GET var. Тем не менее, я все еще получаю ту же ошибку, указанную выше.

Ответы [ 3 ]

8 голосов
/ 15 ноября 2011

Я бы попробовал использовать Knox вместе с Node.Js.Известно, что это отличная комбинация, а также сама использует библиотеку Node.JS Crypto, которая является своего рода попыткой сэкономить ваше время:)

Подробнее здесь: https://github.com/LearnBoost/knox

Чем вы можете просто сделать что-то вроде:

var knox = require('knox');
var s3Client = knox.createClient({
    key: 'XXX',
    secret: 'XXX',
    bucket: 'XXX'
});

var expires = new Date();
expires.setMinutes(expires.getMinutes() + 30);
var url =  s3Client.signedUrl(filename, expires);

Редактировать: Вы также можете заглянуть в Knox и просто проверить, что делает функция SignUrl и реализовать это самостоятельно.может добавить к вызову auth.signQuery дополнительную опцию под названием amazonHeaders:

Client.prototype.signedUrl = function(filename, expiration){
  var epoch = Math.floor(expiration.getTime()/1000);
  var signature = auth.signQuery({
    amazonHeaders: 'response-content-disposition:attachment',
    secret: this.secret,
    date: epoch,
    resource: '/' + this.bucket + url.parse(filename).pathname
  });

  return this.url(filename) +
    '?Expires=' + epoch +
    '&AWSAccessKeyId=' + this.key +
    '&Signature=' + encodeURIComponent(signature);
};

Shai.

3 голосов
/ 15 ноября 2011

может быть, слишком много новых строк?

var stringToSign ='GET\n\n\n' + date + '\n\n' + resource;

Если какая-то помощь здесь, это мусорная реализация PHP, которая определенно работает:

class myS3Helper{
public function getSignedImageLink($timeout = 1800)
    {

        $now = new Zend_Date(); //Gives us a time object that is set to NOW
        $now->setTimezone('UTC'); //Set to UTC a-la AWS requirements
        $now->addSecond($timeout);
        $expirationTime = $now->getTimestamp(); //returns unix timestamp representation of the time.

        $signature = urlencode(
                base64_encode(
                        hash_hmac(
                                'sha1', $this->_generateStringToSign($expirationTime),
                                $my_aws_secretkey, 
                                true
                                )
                        )
                );

        //FIXME make this less ugly when I know it works
        $url = 'https://';
        $url .= Zend_Service_Amazon_S3::S3_ENDPOINT; //e.g s3.amazonaws.com
        $url .= $this->_getImagePath(); //e.g /mybucket/myFirstCar.jpg
        $url .='?AWSAccessKeyId=' . $my_aws_key;
        $url .='&Signature=' . $signature; //signature as returned by below function
        $url .='&Expires=' . $expirationTime;

        return $url;


    }

    protected function _generateStringToSign($expires)
    {   

        $string = "GET\n"; //Methods
        $string .= "\n";
        $string .= "\n";
        $string .= "$expires\n"; //Expires
        $string .= $this->_getImagePath();

        return $string;
    }

}

РЕДАКТИРОВАТЬ -

Посмотрите на этот код загрузки node.js s3, (он не мой, но нашел его на моем Mac - так что если кто-то может приписать его кому-то, дайте мне знать, и я сделаю реквизит). Надеюсь, это поможет (в третий раз повезет)

https://gist.github.com/1370593

0 голосов
/ 03 июля 2017

Моя реализация с использованием AWS-SDK и Rx.

import AWS from "aws-sdk"
import Rx from 'rx'

/*
* Credentials could be loaded from env variables
* http://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/loading-node-credentials-environment.html
* */

const s3 = new AWS.S3({apiVersion: '2006-03-01'});

export function getS3SignedImage(objectKey) {
    return Rx.Observable.create(function (observer) {
        s3.getSignedUrl('getObject',{
            Bucket: process.env.AWS_BUCKET,
            Key: objectKey
        }, (err, data) => {
            if (err) {
                return observer.onError(err);
            }
            observer.onNext(data);
            observer.onCompleted();
        });
    });
}
...