【发布时间】:2017-12-06 18:53:04
【问题描述】:
我收到签名版本错误
回复:InvalidRequest不支持您提供的授权机制。请使用 AWS4-HMAC-SHA256.5BD2DFA1B0ECAA01HBXUWHbeNnB8Lfp+YNgiCxQyPInN+YlIfJtsmScVY6abqNtKYdhqCFU4xPnoTRZkqaKZLnAOqlY=
这里是代码
由 Joe Danziger (joe@ajaxcf.com) 在以下人员的大力帮助下撰写 亚马逊 S3 论坛上的 dorioo。有关更多信息,请参阅自述文件 用法和方法的详细信息。 感谢 Steve Hicks 对存储桶 ACL 的更新。 感谢 Carlos Gallupa 提供欧盟存储位置更新。 感谢 Joel Greutman 对 getObject 链接的修复。 感谢 Jerad Sloan 的缓存控制标头。
1.8 版 - 发布时间:2010 年 7 月 27 日 1.9 版 - 发布时间:2011 年 1 月 6 日 --->
<cfset variables.accessKeyId = "">
<cfset variables.secretAccessKey = "">
<cffunction name="init" access="public" returnType="s3" output="false"
hint="Returns an instance of the CFC initialized.">
<cfargument name="accessKeyId" type="string" required="true" hint="Amazon S3 Access Key ID.">
<cfargument name="secretAccessKey" type="string" required="true" hint="Amazon S3 Secret Access Key.">
<cfset variables.accessKeyId = arguments.accessKeyId>
<cfset variables.secretAccessKey = arguments.secretAccessKey>
<cfreturn this>
</cffunction>
<cffunction name="HMAC_SHA1" returntype="binary" access="private" output="false" hint="NSA SHA-1 Algorithm">
<cfargument name="signKey" type="string" required="true" />
<cfargument name="signMessage" type="string" required="true" />
<cfset var jMsg = JavaCast("string",arguments.signMessage).getBytes("iso-8859-1") />
<cfset var jKey = JavaCast("string",arguments.signKey).getBytes("iso-8859-1") />
<cfset var key = createObject("java","javax.crypto.spec.SecretKeySpec") />
<cfset var mac = createObject("java","javax.crypto.Mac") />
<cfset key = key.init(jKey,"HmacSHA1") />
<cfset mac = mac.getInstance(key.getAlgorithm()) />
<cfset mac.init(key) />
<cfset mac.update(jMsg) />
<cfreturn mac.doFinal() />
</cffunction>
<cffunction name="createSignature" returntype="string" access="public" output="false">
<cfargument name="stringIn" type="string" required="true" />
<!--- Replace "\n" with "chr(10) to get a correct digest --->
<cfset var fixedData = replace(arguments.stringIn,"\n","#chr(10)#","all")>
<!--- Calculate the hash of the information --->
<!---<cfset var digest = HMAC_SHA1(variables.secretAccessKey,fixedData)>--->
<cfset var digest = HMAC( fixedData, variables.secretAccessKey, 'HmacSHA256' )>
<!--- fix the returned data to be a proper signature --->
<cfset var signature = ToBase64("#digest#")>
<cfreturn signature>
</cffunction>
<cffunction name="getBuckets" access="public" output="false" returntype="array"
description="List all available buckets.">
<cfset var data = "">
<cfset var bucket = "">
<cfset var buckets = "">
<cfset var thisBucket = "">
<cfset var allBuckets = "">
<cfset var dateTimeString = GetHTTPTimeString(Now())>
<!--- Create a canonical string to send --->
<cfset var cs = "GET\n\n\n#dateTimeString#\n/">
<!--- Create a proper signature --->
<cfset var signature = createSignature(cs)>
<!--- get all buckets via REST --->
<cfhttp method="GET" url="http://s3.amazonaws.com">
<cfhttpparam type="header" name="Date" value="#dateTimeString#">
<cfhttpparam type="header" name="Authorization" value="AWS #variables.accessKeyId#:#signature#">
</cfhttp>
<cfset data = xmlParse(cfhttp.FileContent)>
<cfset buckets = xmlSearch(data, "//:Bucket")>
<!--- create array and insert values from XML --->
<cfset allBuckets = arrayNew(1)>
<cfloop index="x" from="1" to="#arrayLen(buckets)#">
<cfset bucket = buckets[x]>
<cfset thisBucket = structNew()>
<cfset thisBucket.Name = bucket.Name.xmlText>
<cfset thisBucket.CreationDate = bucket.CreationDate.xmlText>
<cfset arrayAppend(allBuckets, thisBucket)>
</cfloop>
<cfreturn allBuckets>
</cffunction>
<cffunction name="putBucket" access="public" output="false" returntype="boolean"
description="Creates a bucket.">
<cfargument name="bucketName" type="string" required="true">
<cfargument name="acl" type="string" required="false" default="public-read">
<cfargument name="storageLocation" type="string" required="false" default="">
<cfset var strXML = "">
<cfset var dateTimeString = GetHTTPTimeString(Now())>
<!--- Create a canonical string to send based on operation requested --->
<cfset var cs = "PUT\n\ntext/html\n#dateTimeString#\nx-amz-acl:#arguments.acl#\n/#arguments.bucketName#">
<!--- Create a proper signature --->
<cfset var signature = createSignature(cs)>
<cfif compare(arguments.storageLocation,'')>
<cfsavecontent variable="strXML">
<CreateBucketConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><LocationConstraint>#arguments.storageLocation#</LocationConstraint></CreateBucketConfiguration>
</cfsavecontent>
<cfelse>
<cfset strXML = "">
</cfif>
<!--- put the bucket via REST --->
<cfhttp method="PUT" url="http://s3.amazonaws.com/#arguments.bucketName#" charset="utf-8">
<cfhttpparam type="header" name="Content-Type" value="text/html">
<cfhttpparam type="header" name="Date" value="#dateTimeString#">
<cfhttpparam type="header" name="x-amz-acl" value="#arguments.acl#">
<cfhttpparam type="header" name="Authorization" value="AWS #variables.accessKeyId#:#signature#">
<cfhttpparam type="body" value="#trim(strXML)#">
</cfhttp>
<cfreturn true>
</cffunction>
<cffunction name="getBucket" access="public" output="false" returntype="array"
description="Creates a bucket.">
<cfargument name="bucketName" type="string" required="yes">
<cfargument name="prefix" type="string" required="false" default="">
<cfargument name="marker" type="string" required="false" default="">
<cfargument name="maxKeys" type="string" required="false" default="">
<cfargument name="showVersions" type="boolean" required="false" default="false">
<cfset var cs = "">
<cfset var data = "">
<cfset var content = "">
<cfset var contents = "">
<cfset var version = "">
<cfset var versions = "">
<cfset var signature = "">
<cfset var versioning = "">
<cfset var prefixString = "">
<cfset var markerString = "">
<cfset var maxKeysString = "">
<cfset var thisContent = "">
<cfset var allContents = "">
<cfset var thisVersion = "">
<cfset var allVersions = "">
<cfset var dateTimeString = GetHTTPTimeString(Now())>
<!--- add proper versioning call if requested --->
<cfif arguments.showVersions>
<cfset versioning = "?versions">
</cfif>
<!--- Create a canonical string to send --->
<cfset cs = "GET\n\n\n#dateTimeString#\n/#arguments.bucketName##versioning#">
<!--- Create a proper signature --->
<cfset signature = createSignature(cs)>
<!--- get the bucket via REST --->
<cfif arguments.showVersions>
<cfif compare(arguments.prefix,'')>
<cfset prefixString = "&prefix=#arguments.prefix#">
</cfif>
<cfif compare(arguments.marker,'')>
<cfset markerString = "&marker=#arguments.marker#">
</cfif>
<cfif isNumeric(arguments.maxKeys)>
<cfset maxKeysString = "&max-keys=#arguments.maxKeys#">
</cfif>
<cfhttp method="GET" url="http://s3.amazonaws.com/#arguments.bucketName#?versions#prefixString##markerString##maxKeysString#">
<cfhttpparam type="header" name="Date" value="#dateTimeString#">
<cfhttpparam type="header" name="Authorization" value="AWS #variables.accessKeyId#:#signature#">
</cfhttp>
<cfelse>
<cfhttp method="GET" url="http://s3.amazonaws.com/#arguments.bucketName#">
<cfhttpparam type="header" name="Date" value="#dateTimeString#">
<cfhttpparam type="header" name="Authorization" value="AWS #variables.accessKeyId#:#signature#">
<cfif compare(arguments.prefix,'')>
<cfhttpparam type="URL" name="prefix" value="#arguments.prefix#">
</cfif>
<cfif compare(arguments.marker,'')>
<cfhttpparam type="URL" name="marker" value="#arguments.marker#">
</cfif>
<cfif isNumeric(arguments.maxKeys)>
<cfhttpparam type="URL" name="max-keys" value="#arguments.maxKeys#">
</cfif>
</cfhttp>
</cfif>
<cfset data = xmlParse(cfhttp.FileContent)>
<cfif arguments.showVersions>
<cfset versions = xmlSearch(data, "//:Version")>
<!--- create array and insert values from XML --->
<cfset allVersions = arrayNew(1)>
<cfloop index="x" from="1" to="#arrayLen(versions)#">
<cfset version = versions[x]>
<cfset thisVersion = structNew()>
<cfset thisVersion.Key = version.Key.xmlText>
<cfset thisVersion.VersionID = version.VersionID.xmlText>
<cfset thisVersion.isLatest = version.IsLatest.xmlText>
<cfset thisVersion.LastModified = version.LastModified.xmlText>
<cfset thisVersion.Size = version.Size.xmlText>
<cfset thisVersion.StorageClass = version.StorageClass.xmlText>
<cfset arrayAppend(allVersions, thisVersion)>
</cfloop>
<cfreturn allVersions>
<cfelse>
<cfset contents = xmlSearch(data, "//:Contents")>
<!--- create array and insert values from XML --->
<cfset allContents = arrayNew(1)>
<cfloop index="x" from="1" to="#arrayLen(contents)#">
<cfset content = contents[x]>
<cfset thisContent = structNew()>
<cfset thisContent.Key = content.Key.xmlText>
<cfset thisContent.LastModified = content.LastModified.xmlText>
<cfset thisContent.Size = content.Size.xmlText>
<cfset thisContent.StorageClass = content.StorageClass.xmlText>
<cfset arrayAppend(allContents, thisContent)>
</cfloop>
<cfreturn allContents>
</cfif>
</cffunction>
<cffunction name="deleteBucket" access="public" output="false" returntype="boolean"
description="Deletes a bucket.">
<cfargument name="bucketName" type="string" required="yes">
<cfset var dateTimeString = GetHTTPTimeString(Now())>
<!--- Create a canonical string to send based on operation requested --->
<cfset var cs = "DELETE\n\n\n#dateTimeString#\n/#arguments.bucketName#">
<!--- Create a proper signature --->
<cfset var signature = createSignature(cs)>
<!--- delete the bucket via REST --->
<cfhttp method="DELETE" url="http://s3.amazonaws.com/#arguments.bucketName#" charset="utf-8">
<cfhttpparam type="header" name="Date" value="#dateTimeString#">
<cfhttpparam type="header" name="Authorization" value="AWS #variables.accessKeyId#:#signature#">
</cfhttp>
<cfreturn true>
</cffunction>
<cffunction name="putObject" access="public" output="false" returntype="string"
description="Puts an object into a bucket.">
<cfargument name="bucketName" type="string" required="yes">
<cfargument name="fileKey" type="string" required="yes">
<cfargument name="contentType" type="string" required="yes">
<cfargument name="HTTPtimeout" type="numeric" required="no" default="300">
<cfargument name="cacheControl" type="boolean" required="false" default="true">
<cfargument name="cacheDays" type="numeric" required="false" default="30">
<cfargument name="acl" type="string" required="no" default="public-read">
<cfargument name="storageClass" type="string" required="no" default="STANDARD">
<cfargument name="keyName" type="string" required="no" default="#arguments.fileKey#">
<cfargument name="uploadDir" type="string" required="no" default="#ExpandPath('./')#">
<cfset var versionID = "">
<cfset var binaryFileData = "">
<cfset var dateTimeString = GetHTTPTimeString(Now())>
<cfset var cs = "">
<cfset var signature = "">
<!--- if keyName submitted blank, use fileKey --->
<cfif not compare(arguments.keyName,'')>
<cfset arguments.keyName = arguments.fileKey>
</cfif>
<!--- Create a canonical string to send --->
<cfset cs = "PUT\n\n#arguments.contentType#\n#dateTimeString#\nx-amz-acl:#arguments.acl#\nx-amz-storage-class:#arguments.storageClass#\n/#arguments.bucketName#/#arguments.keyName#">
<!--- Create a proper signature --->
<cfset signature = createSignature(cs)>
<!--- Read the image data into a variable --->
<cffile action="readBinary" file="#arguments.uploadDir##arguments.fileKey#" variable="binaryFileData">
<!--- Send the file to amazon. The "X-amz-acl" controls the access properties of the file --->
<cfhttp method="PUT" url="https://cinetimes.s3.amazonaws.com/#arguments.bucketName#/#arguments.keyName#" timeout="#arguments.HTTPtimeout#">
<cfhttpparam type="header" name="Authorization" value="AWS #variables.accessKeyId#:#signature#">
<cfhttpparam type="header" name="Content-Type" value="#arguments.contentType#">
<cfhttpparam type="header" name="Date" value="#dateTimeString#">
<cfhttpparam type="header" name="x-amz-acl" value="#arguments.acl#">
<cfhttpparam type="header" name="x-amz-storage-class" value="#arguments.storageClass#">
<cfhttpparam type="body" value="#binaryFileData#">
<cfif arguments.cacheControl>
<cfhttpparam type="header" name="Cache-Control" value="max-age=2592000">
<cfhttpparam type="header" name="Expires" value="#DateFormat(now()+arguments.cacheDays,'ddd, dd mmm yyyy')# #TimeFormat(now(),'H:MM:SS')# GMT">
</cfif>
</cfhttp>
<cfdump var="https://cinetimes.s3.amazonaws.com/#arguments.bucketName#/#arguments.keyName#">
<cfdump var="#signature#">
<cfdump var="#cfhttp#"><cfabort>
<cftry>
<cfset versionID = cfhttp.responseHeader['x-amz-version-id']>
<cfcatch></cfcatch>
</cftry>
<cfreturn versionID>
</cffunction>
<cffunction name="getObject" access="public" output="false" returntype="string"
description="Returns a link to an object.">
<cfargument name="bucketName" type="string" required="yes">
<cfargument name="fileKey" type="string" required="yes">
<cfargument name="minutesValid" type="string" required="false" default="60">
<cfset var timedAmazonLink = "">
<cfset var epochTime = DateDiff("s", DateConvert("utc2Local", "January 1 1970 00:00"), now()) + (arguments.minutesValid * 60)>
<!--- Create a canonical string to send --->
<cfset var cs = "GET\n\n\n#epochTime#\n/#arguments.bucketName#/#arguments.fileKey#">
<!--- Create a proper signature --->
<cfset var signature = createSignature(cs)>
<!--- Create the timed link for the image --->
<cfset timedAmazonLink = "http://s3.amazonaws.com/#arguments.bucketName#/#arguments.fileKey#?AWSAccessKeyId=#URLEncodedFormat(variables.accessKeyId)#&Expires=#epochTime#&Signature=#URLEncodedFormat(signature)#">
<cfreturn timedAmazonLink>
</cffunction>
<cffunction name="deleteObject" access="public" output="false" returntype="boolean"
description="Deletes an object.">
<cfargument name="bucketName" type="string" required="yes">
<cfargument name="fileKey" type="string" required="yes">
<cfset var dateTimeString = GetHTTPTimeString(Now())>
<!--- Create a canonical string to send based on operation requested --->
<cfset var cs = "DELETE\n\n\n#dateTimeString#\n/#arguments.bucketName#/#arguments.fileKey#">
<!--- Create a proper signature --->
<cfset var signature = createSignature(cs)>
<!--- delete the object via REST --->
<cfhttp method="DELETE" url="http://s3.amazonaws.com/#arguments.bucketName#/#arguments.fileKey#">
<cfhttpparam type="header" name="Date" value="#dateTimeString#">
<cfhttpparam type="header" name="Authorization" value="AWS #variables.accessKeyId#:#signature#">
</cfhttp>
<cfreturn true>
</cffunction>
<cffunction name="copyObject" access="public" output="false" returntype="boolean"
description="Copies an object.">
<cfargument name="oldBucketName" type="string" required="yes">
<cfargument name="oldFileKey" type="string" required="yes">
<cfargument name="newBucketName" type="string" required="yes">
<cfargument name="newFileKey" type="string" required="yes">
<cfset var dateTimeString = GetHTTPTimeString(Now())>
<!--- Create a canonical string to send based on operation requested --->
<cfset var cs = "PUT\n\napplication/octet-stream\n#dateTimeString#\nx-amz-copy-source:/#arguments.oldBucketName#/#arguments.oldFileKey#\n/#arguments.newBucketName#/#arguments.newFileKey#">
<!--- Create a proper signature --->
<cfset var signature = createSignature(cs)>
<cfif compare(arguments.oldBucketName,arguments.newBucketName) or compare(arguments.oldFileKey,arguments.newFileKey)>
<!--- delete the object via REST --->
<cfhttp method="PUT" url="http://s3.amazonaws.com/#arguments.newBucketName#/#arguments.newFileKey#">
<cfhttpparam type="header" name="Date" value="#dateTimeString#">
<cfhttpparam type="header" name="x-amz-copy-source" value="/#arguments.oldBucketName#/#arguments.oldFileKey#">
<cfhttpparam type="header" name="Authorization" value="AWS #variables.accessKeyId#:#signature#">
</cfhttp>
<cfreturn true>
<cfelse>
<cfreturn false>
</cfif>
</cffunction>
<cffunction name="renameObject" access="public" output="false" returntype="boolean"
description="Renames an object by copying then deleting original.">
<cfargument name="oldBucketName" type="string" required="yes">
<cfargument name="oldFileKey" type="string" required="yes">
<cfargument name="newBucketName" type="string" required="yes">
<cfargument name="newFileKey" type="string" required="yes">
<cfif compare(arguments.oldBucketName,arguments.newBucketName) or compare(arguments.oldFileKey,arguments.newFileKey)>
<cfset copyObject(arguments.oldBucketName,arguments.oldFileKey,arguments.newBucketName,arguments.newFileKey)>
<cfset deleteObject(arguments.oldBucketName,arguments.oldFileKey)>
<cfreturn true>
<cfelse>
<cfreturn false>
</cfif>
</cffunction>
<cffunction name="objectExists" access="public" output="false" returntype="boolean"
description="Tests if object exists in bucket.">
<cfargument name="bucketName" type="string" required="yes">
<cfargument name="fileKey" type="string" required="yes">
<cfset var dateTimeString = GetHTTPTimeString(Now())>
<cfset var result = "">
<cfset var found = 0>
<!--- Create a canonical string to send --->
<cfset cs = "HEAD\n\n\n#dateTimeString#\n/#arguments.bucketName#/#arguments.fileKey#">
<!--- Create a proper signature --->
<cfset var signature = createSignature(cs)>
<!--- HEAD the object via REST --->
<cfhttp method="HEAD" url="http://s3.amazonaws.com/#arguments.bucketName#/#arguments.fileKey#" result="result">
<cfhttpparam type="header" name="Date" value="#dateTimeString#">
<cfhttpparam type="header" name="Authorization" value="AWS #variables.accessKeyId#:#signature#">
</cfhttp>
<cfswitch expression="#Left(result.statusCode,3)#">
<cfcase value="200">
<cfset found = 1>
</cfcase>
<cfcase value="404">
<cfset found = 0>
</cfcase>
</cfswitch>
<cfreturn found>
</cffunction>
<cffunction name="getBucketVersioning" access="public" output="false" returntype="string"
description="Determines versioning setting for a bucket.">
<cfargument name="bucketName" type="string" required="yes">
<cfset var data = "">
<cfset var result = "">
<cfset var dateTimeString = GetHTTPTimeString(Now())>
<!--- Create a canonical string to send --->
<cfset var cs = "GET\n\n\n#dateTimeString#\n/#arguments.bucketName#?versioning">
<!--- Create a proper signature --->
<cfset var signature = createSignature(cs)>
<!--- get the bucket via REST --->
<cfhttp method="GET" url="http://s3.amazonaws.com/#arguments.bucketName#?versioning">
<cfhttpparam type="header" name="Date" value="#dateTimeString#">
<cfhttpparam type="header" name="Authorization" value="AWS #variables.accessKeyId#:#signature#">
</cfhttp>
<cfset data = xmlParse(cfhttp.FileContent)>
<cftry>
<cfset result = data.VersioningConfiguration.Status.xmlText>
<cfcatch><cfset result = "Disabled"></cfcatch>
</cftry>
<cfreturn result>
</cffunction>
<cffunction name="setBucketVersioning" access="public" output="false" returntype="boolean"
description="Sets versioning on a bucket.">
<cfargument name="bucketName" type="string" required="true">
<cfargument name="versioning" type="string" required="false" default="Enabled">
<cfset var strXML = "">
<cfset var dateTimeString = GetHTTPTimeString(Now())>
<!--- Create a canonical string to send based on operation requested --->
<cfset var cs = "PUT\n\ntext/html\n#dateTimeString#\n/#arguments.bucketName#?versioning">
<!--- Create a proper signature --->
<cfset var signature = createSignature(cs)>
<cfsavecontent variable="strXML">
<VersioningConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Status><cfoutput>#arguments.versioning#</cfoutput></Status></VersioningConfiguration>
</cfsavecontent>
<!--- put the bucket via REST --->
<cfhttp method="PUT" url="http://s3.amazonaws.com/#arguments.bucketName#?versioning" charset="utf-8">
<cfhttpparam type="header" name="Content-Type" value="text/html">
<cfhttpparam type="header" name="Date" value="#dateTimeString#">
<cfhttpparam type="header" name="Authorization" value="AWS #variables.accessKeyId#:#signature#">
<cfhttpparam type="body" value="#trim(strXML)#">
</cfhttp>
<cfreturn true>
</cffunction>
【问题讨论】:
-
我相信这是因为您尝试访问 S3 存储桶的可用区仅支持 AWS 签名 v4。你为什么不直接使用内置的 S3 访问,例如s3:// 在文件路径中还是通过映射?我相信 Lucee 5 支持带有最新 S3 扩展的签名 V4。
标签: amazon-s3 coldfusion coldfusion-10 railo lucee