如何让 node.js 服务器对来自亚马逊网络服务的请求进行签名以获取优质上传者?

Posted

技术标签:

【中文标题】如何让 node.js 服务器对来自亚马逊网络服务的请求进行签名以获取优质上传者?【英文标题】:How do I get the node.js server to sign the request from amazon web services for fine uploader? 【发布时间】:2014-04-16 21:45:20 【问题描述】:

在尝试将图像上传到 AWS 时,让 node.js 对来自 AWS 的请求进行签名时遇到问题。这是我在 Chrome 控制台中得到的:

[Fine Uploader 4.4.0] Parsing template s3.jquery.fineuploader-4.4.0.js:198
[Fine Uploader 4.4.0] Template parsing complete s3.jquery.fineuploader-4.4.0.js:198
[Fine Uploader 4.4.0] Rendering template in DOM. s3.jquery.fineuploader-4.4.0.js:198
[Fine Uploader 4.4.0] Template rendering complete s3.jquery.fineuploader-4.4.0.js:198
[Fine Uploader 4.4.0] Received 1 files or inputs. s3.jquery.fineuploader-4.4.0.js:198
[Fine Uploader 4.4.0] Attempting to validate image. s3.jquery.fineuploader-4.4.0.js:198
[Fine Uploader 4.4.0] Submitting S3 signature request for 0 s3.jquery.fineuploader-4.4.0.js:198
[Fine Uploader 4.4.0] Sending POST request for 0 s3.jquery.fineuploader-4.4.0.js:198
OPTIONS file://192.168.2.16/fine-uploader/_dist/s3/server.js No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access. s3.jquery.fineuploader-4.4.0.js:3657
XMLHttpRequest cannot load file://192.168.2.16/fine-uploader/_dist/s3/server.js. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access. default.html:1
[Fine Uploader 4.4.0] POST request for 0 has failed - response code 0 s3.jquery.fineuploader-4.4.0.js:203
[Fine Uploader 4.4.0] Received an empty or invalid response from the server! s3.jquery.fineuploader-4.4.0.js:203
[Fine Uploader 4.4.0] Policy signing failed.  Received an empty or invalid response from the server! s3.jquery.fineuploader-4.4.0.js:203
[Fine Uploader 4.4.0] Received response status 0 with body:  s3.jquery.fineuploader-4.4.0.js:198

我意识到我在签名方面做错了,但由于文档非常不清楚,我不知道下一步该做什么。这是我的 HTML、server.js 和 CORS:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="http://code.jquery.com/jquery-1.10.2.js" type="text/javascript">    </script>
<link href="../../s3/fineuploader-4.4.0.min.css" rel="stylesheet">
<script src="../../s3/s3.jquery.fineuploader-4.4.0.js"></script>
<script>
// Wait until the DOM is 'ready'
$(document).ready(function () 
    $("#fine-uploader").fineUploaderS3(
        debug: true,
        request: 
            endpoint: 'fineuploader1.s3.amazonaws.com'
            // accessKey: 'AKIAICPB2GGIC4VOF2WQ'
        ,
        signature: 
            endpoint: '../../s3/server.js'
        ,
        uploadSuccess: 
            endpoint: '/s3/success'
        ,
        iframeSupport: 
            localBlankPagePath: '/success.html'
        ,
        retry: 
           enableAuto: true // defaults to false
        ,
        deleteFile: 
            enabled: true,
            endpoint: '/s3handler'
        
    );
);
</script>
<script type="text/template" id="qq-template">
    <div class="qq-uploader-selector qq-uploader">
        <div class="qq-total-progress-bar-container-selector qq-total-progress-bar-container">
            <div class="qq-total-progress-bar-selector qq-progress-bar qq-total-progress-bar"></div>
        </div>
        <div class="qq-upload-drop-area-selector qq-upload-drop-area" qq-hide-dropzone>
            <span>Drop files here to upload</span>
        </div>
        <div class="qq-upload-button-selector qq-upload-button">
            <div>Upload a file</div>
        </div>
        <span class="qq-drop-processing-selector qq-drop-processing">
            <span>Processing dropped files...</span>
            <span class="qq-drop-processing-spinner-selector qq-drop-processing-spinner"></span>
        </span>
        <ul class="qq-upload-list-selector qq-upload-list">
            <li>
                <div class="qq-progress-bar-container-selector">
                    <div class="qq-progress-bar-selector qq-progress-bar"></div>
                </div>
                <span class="qq-upload-spinner-selector qq-upload-spinner"></span>
                <span class="qq-edit-filename-icon-selector qq-edit-filename-icon"></span>
                <span class="qq-upload-file-selector qq-upload-file"></span>
                <input class="qq-edit-filename-selector qq-edit-filename" tabindex="0" type="text">
                <span class="qq-upload-size-selector qq-upload-size"></span>
                <a class="qq-upload-cancel-selector qq-upload-cancel" href="#">Cancel</a>
                <a class="qq-upload-retry-selector qq-upload-retry" href="#">Retry</a>
                <a class="qq-upload-delete-selector qq-upload-delete" href="#">Delete</a>
                <span class="qq-upload-status-text-selector qq-upload-status-text"></span>
            </li>
        </ul>
    </div>
</script>

<title>Fine Uploader default UI</title>
</head>
<body>
<div id="fine-uploader"></div>
</body>
</html>

server.js:

var express = require("express"),
crypto = require("crypto"),
aws = require("aws-sdk"),
app = express(),
clientSecretKey = process.env.CLIENT_SECRET_KEY,
// These two keys are only needed if you plan on using the AWS SDK
serverPublicKey = process.env.SERVER_PUBLIC_KEY,
serverSecretKey = process.env.SERVER_SECRET_KEY,
// Set these two values to match your environment
expectedBucket = "fineuploadertest1",
expectedMaxSize = 15000000,
s3;


// Init S3, given your server-side keys.  Only needed if using the AWS SDK.
aws.config.update(
accessKeyId: serverPublicKey,
secretAccessKey: serverSecretKey
);
s3 = new aws.S3();


app.use(express.bodyParser());
app.use(express.static(__dirname)); //only needed if serving static content as well
app.listen(8000);

// Handles all signature requests and the success request FU S3 sends after the file is in S3
// You will need to adjust these paths/conditions based on your setup.
app.post("\\192.168.2.16\fine-uploader\_dist\s3\server.js", function(req, res) 
if (req.query.success !== undefined) 
    verifyFileInS3(req, res);

else 
    signRequest(req, res);

);

// Handles the standard DELETE (file) request sent by Fine Uploader S3.
// Omit if you don't want to support this feature.
app.delete("../../s3/server.js", function(req, res) 
deleteFile(req.query.bucket, req.query.key, function(err) 
    if (err) 
        console.log("Problem deleting file: " + err);
        res.status(500);
    

    res.end();
);
);

// Signs any requests.  Delegate to a more specific signer based on type of request.
function signRequest(req, res) 
if (req.body.headers) 
    signRestRequest(req, res);

else 
    signPolicy(req, res);



// Signs multipart (chunked) requests.  Omit if you don't want to support chunking.
function signRestRequest(req, res) 
var stringToSign = req.body.headers,
    signature = crypto.createHmac("sha1", clientSecretKey)
    .update(stringToSign)
    .digest("base64");

var jsonResponse = 
    signature: signature
;

res.setHeader("Content-Type", "application/json");

if (isValidRestRequest(stringToSign)) 
    res.end(JSON.stringify(jsonResponse));

else 
    res.status(400);
    res.end(JSON.stringify(invalid: true));



// Signs "simple" (non-chunked) upload requests.
function signPolicy(req, res) 
var base64Policy = new Buffer(JSON.stringify(req.body)).toString("base64"),
    signature = crypto.createHmac("sha1", clientSecretKey)
    .update(base64Policy)
    .digest("base64");

var jsonResponse = 
    policy: base64Policy,
    signature: signature
;

res.setHeader("Content-Type", "application/json");

if (isPolicyValid(req.body)) 
    res.end(JSON.stringify(jsonResponse));

else 
    res.status(400);
    res.end(JSON.stringify(invalid: true));



// Ensures the REST request is targeting the correct bucket.
// Omit if you don't want to support chunking.
function isValidRestRequest(headerStr) 
return new RegExp("\/" + expectedBucket + "\/.+$").exec(headerStr) != null;


// Ensures the policy document associated with a "simple" (non-chunked) request is
// targeting the correct bucket and the max-size is as expected.
// Omit the parsedMaxSize-related code if you don't have a max file size.
function isPolicyValid(policy) 
var bucket, parsedMaxSize;

policy.conditions.forEach(function(condition) 
    if (condition.bucket) 
        bucket = condition.bucket;
    
    else if (condition instanceof Array && condition[0] === "content-length-range") 
        parsedMaxSize = condition[2];
    
);

return bucket === expectedBucket && parsedMaxSize === expectedMaxSize.toString();


// After the file is in S3, make sure it isn't too big.
// Omit if you don't have a max file size, or add more logic as required.
function verifyFileInS3(req, res) 
function headReceived(err, data) 
    if (err) 
        res.status(500);
        console.log(err);
        res.end(JSON.stringify(error: "Problem querying S3!"));
    
    else if (data.ContentLength > expectedMaxSize) 
        res.status(400);
        res.write(JSON.stringify(error: "Too big!"));
        deleteFile(req.body.bucket, req.body.key, function(err) 
            if (err) 
                console.log("Couldn't delete invalid file!");
            

            res.end();
        );
    
    else 
        res.end();
    


callS3("head", 
    bucket: req.body.bucket,
    key: req.body.key
, headReceived);


function deleteFile(bucket, key, callback) 
callS3("delete", 
    bucket: bucket,
    key: key
, callback);


function callS3(type, spec, callback) 
s3[type + "Object"](
    Bucket: spec.bucket,
    Key: spec.key
, callback)

CORS:

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
    <AllowedOrigin>*</AllowedOrigin>
    <AllowedMethod>POST</AllowedMethod>
    <AllowedMethod>PUT</AllowedMethod>
    <AllowedMethod>DELETE</AllowedMethod>
    <MaxAgeSeconds>3000</MaxAgeSeconds>
    <ExposeHeader>ETag</ExposeHeader>
    <AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>

所以根据文档中的说明,http://docs.fineuploader.com/quickstart/03-setting_up_server-s3.html,我将通过下载并运行它来设置服务器。但是,它没有说明签名。我将签名指向 server.js。我不知道还能指点哪里。此页面上有关签名的文档http://docs.fineuploader.com/endpoint_handlers/amazon-s3.html 似乎对我一点帮助都没有,因为我不明白它试图让我做什么才能使其工作。我知道这些可能是愚蠢的问题,但我只需要指出正确的方向,这样我就可以继续前进。我很感激任何帮助。非常感谢。如果我能提供更多信息,请告诉我。

【问题讨论】:

github.com/Widen/fine-uploader-server/blob/master/nodejs/s3/… 有一个 Fine Uploader S3 的 node.js 示例,这在多个地方都有引用,例如在 S3 端点处理程序文档页面中。 正确。这与我从这些说明下载的上述 server.js 文件中发布的内容相同,docs.fineuploader.com/quickstart/03-setting_up_server-s3.html。我是否错过了我应该用它做的其他事情? 【参考方案1】:

首先,我猜你没有在你的服务器中设置 CLIENT_SECRET_KEY 环境变量的属性。它应该设置为与在上传器构建期间传递给 Fine Uploader S3 的公钥对应的相同 IAM 用户的密钥。

其次,您应该从适当的网络服务器提供您的网页。我可以从日志消息中告诉你没有这样做。

第三,您为什么要在 JavaScript 选项中注释掉您的访问密钥?这是一个必需的选项。

您的设置可能还有其他问题,但乍一看这些是最明显的问题。只要您正确设置服务器和客户端,提供的节点示例就可以正常工作。

【讨论】:

我注释掉了 accesskey,因为如果我不这样做,我会收到控制台错误“Uncaught SyntaxError: Unexpected identifier”。 Fine Uploader S3 中的这个配置选项绝对没有问题。您不能简单地注释掉所需的选项。您需要确定代码的哪个方面导致异常。 所以我取消了你指出的所需部分的注释,所以现在有一个意外的标识符。我接触的唯一代码在上面列出,所以没有其他任何东西被篡改。这可能只是服务器问题吗?我现在正在与我的 IT 支持人员联系,看看是否为此正确设置了此服务器。这听起来不像是服务器会发生的错误。感谢您指出一些可能的问题,但似乎这些问题并没有解决问题。提前感谢您提供的任何进一步帮助。 正如我所说,您的代码有很多明显的问题,除非您先解决一些更基本的问题,否则很难提供帮助。您需要提供指向您的实时应用的链接,以便我进一步提供帮助。

以上是关于如何让 node.js 服务器对来自亚马逊网络服务的请求进行签名以获取优质上传者?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 node.js 中使用 stroph.js 服务器端

删除“www”。来自 Node.JS 中的域

如何将视频(来自 getUserMedia)发送到 Node.js 服务器?

如何将视频(来自 getUserMedia)发送到 Node.js 服务器?

如何显示来自 Node Js 服务器的图像

AWS EC2 与 AWS API Gateway 上的 Node.js RESTful API 服务器