如何让 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 服务器端
如何将视频(来自 getUserMedia)发送到 Node.js 服务器?