[英]Error connecting to azure blob storage API from R
I am attempting to work with Azure storage via the REST API in R. I'm using the package httr
which overlays Curl. 我试图通过R中的REST API使用Azure存储。我正在使用覆盖Curl的包
httr
。
You can use R-fiddle: http://www.r-fiddle.org/#/fiddle?id=vh8uqGmM 您可以使用R-fiddle: http ://www.r-fiddle.org/#/fiddle? id = vh8uqGmM
library(httr)
requestdate<-format(Sys.time(),"%a, %d %b %Y %H:%M:%S GMT")
url<-"https://preconstuff.blob.core.windows.net/pings?restype=container&comp=list"
sak<-"Q8HvUVJLBJK+wkrIEG6LlsfFo19iDjneTwJxX/KXSnUCtTjgyyhYnH/5azeqa1bluGD94EcPcSRyBy2W2A/fHQ=="
signaturestring<-paste0("GET",paste(rep("\n",12),collapse=""),
"x-ms-date:",requestdate,"
x-ms-version:2009-09-19
/preconstuff/pings
comp:list
restype:container")
headerstuff<-add_headers(Authorization=paste0("SharedKey preconstuff:",
RCurl::base64(digest::hmac(key=sak,
object=enc2utf8(signaturestring),
algo= "sha256"))),
`x-ms-date`=requestdate,
`x-ms-version`= "2009-09-19")
Trying to list blobs: 试图列出blob:
content(GET(url,config = headerstuff, verbose() ))
The MAC signature found in the HTTP request 'Q8HvUVJLBJK+wkrIEG6LlsfFo19iDjneTwJxX/KXSnUCtTjgyyhYnH/5azeqa1bluGD94EcPcSRyBy2W2A/fHQ==' is not the same as any computed signature.
在HTTP请求'Q8HvUVJLBJK + wkrIEG6LlsfFo19iDjneTwJxX /KXSnUCtTjgyyhYnH / 5azeqa1bluGD94EcPcSRyBy2W2A / fHQ =='中找到的MAC签名与任何计算签名不同。
[1] "<?xml version=\"1.0\" encoding=\"utf-8\"?><Error>
<Code>AuthenticationFailed</Code><Message>Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.\nRequestId:1ab26da5-0001-00dc-6ddb-15e35c000000\nTime:2015-03-26T17:51:42.7190620Z</Message>
<AuthenticationErrorDetail>The MAC signature found in the HTTP request 'NTM1ODZjMjhhZmMyZGM3NDM0YTFjZDgwNGE0ODVmMzVjNDhkNjBkNzk1ZjNkZjJjOTNlNjUxYTMwMjRhNzNlYw==' is not the same as any computed signature. Server used following string to sign:
'GET\n\n\n\n\n\n\n\n\n\n\n\nx-ms-date:Thu, 26 Mar 2015 17:52:37 GMT\nx-ms-version:2009-09-19\n/preconstuff/pings\ncomp:list\nrestype:container'.
</AuthenticationErrorDetail></Error>"
-> GET /pings?restype=container&comp=list HTTP/1.1
-> User-Agent: curl/7.39.0 Rcurl/1.95.4.5 httr/0.6.1
-> Host: preconstuff.blob.core.windows.net
-> Accept-Encoding: gzip
-> Accept: application/json, text/xml, application/xml, */*
-> Authorization: SharedKey preconstuff:OTRhNTgzYmY3OTY3M2UzNjk3ODdjMzk3OWM3ZmU0OTA4MWU5NTE2OGYyZGU3YzRjNjQ1M2NkNzY0ZTcyZDRhYQ==
-> x-ms-date: Thu, 26 Mar 2015 17:56:27 GMT
-> x-ms-version: 2009-09-19
->
<- HTTP/1.1 403 Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.
<- Content-Length: 719
<- Content-Type: application/xml
<- Server: Microsoft-HTTPAPI/2.0
<- x-ms-request-id: 3d47770c-0001-0085-2313-6d466f000000
<- Date: Thu, 26 Mar 2015 17:56:27 GMT
<-
Googling for this issue doesn't seem to yield a consistent cause, but it's likely due to bad formatting / request structure on my part. 谷歌搜索这个问题似乎没有产生一致的原因,但这可能是由于我的格式/请求结构不好。 To that end I checked:
为此我检查了:
encoding="Base64"
in
headerstuff
further to an
MSDN forum question but the same error message was returned
headerstuff
添加了
encoding="Base64"
,进一步
headerstuff
了
MSDN论坛问题,但返回了相同的错误消息
sak
) as the encryption key) version of the UTF8 converted signaturestring
as the value to be used in the SharedKey authorisation. sak
))作为加密密钥)UTF8转换的signaturestring
字符串的版本作为要在SharedKey授权中使用的值。 Is there anything obviously wrong? 有什么明显的错误吗? Are there other things to check?
还有其他事情要检查吗? Does the code work for others?
代码是否适用于其他人?
Looks like your problem is with the key. 看起来你的问题是关键。 The string of the key you have provided is actually base64 encoded.
您提供的密钥字符串实际上是base64编码的。 You need to decode that to the raw vector before you use it to sign the request.
在使用原始向量对签名请求进行签名之前,需要将其解码。 For example:
例如:
url<-"https://preconstuff.blob.core.windows.net/pings?restype=container&comp=list"
sak<-"Q8HvUVJLBJK+wkrIEG6LlsfFo19iDjneTwJxX/KXSnUCtTjgyyhYnH/5azeqa1bluGD94EcPcSRyBy2W2A/fHQ=="
requestdate<-format(Sys.time(),"%a, %d %b %Y %H:%M:%S %Z", tz="GMT")
signaturestring<-paste0("GET",paste(rep("\n",12),collapse=""),
"x-ms-date:",requestdate,"
x-ms-version:2009-09-19
/preconstuff/pings
comp:list
restype:container")
headerstuff<-add_headers(Authorization=paste0("SharedKey preconstuff:",
RCurl::base64(digest::hmac(key=RCurl::base64Decode(sak, mode="raw"),
object=enc2utf8(signaturestring),
algo= "sha256", raw=TRUE))),
`x-ms-date`=requestdate,
`x-ms-version`= "2009-09-19")
content(GET(url,config = headerstuff, verbose() ))
There are no more authentication errors this way, though no blobs are listed. 虽然没有列出blob,但这种方式不再存在身份验证错误。 Perhaps that's a different issue.
也许这是一个不同的问题。
Also, I changed the way the date/time was created to more "safely" change the local time to GMT. 此外,我更改了创建日期/时间的方式,以更“安全”地将当地时间更改为GMT。
It looks like you are using the key of your account directly in the Authorization header. 您似乎直接在Authorization标头中使用了您帐户的密钥。 To authenticate a request, you must sign the request with the key for the account that is making the request and pass that signature as part of the request.
要对请求进行身份验证,您必须使用发出请求的帐户的密钥对请求进行签名,并将该签名作为请求的一部分传递。 Please see Authentication for the Azure Storage Services for more information on how to construct the Authorization header.
有关如何构建Authorization标头的详细信息,请参阅Azure存储服务的身份验证 。
Please also note that the service returns StringToSign in the error response. 另请注意,该服务在错误响应中返回StringToSign。 So, what your code should have done is to apply the following formula to StringToSign="GET\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\nx-ms-date:Wed, 25 Mar 2015 22:24:12 GMT\\nx-ms-version:2014-02-14\\n/preconstuff/pings\\ncomp:list\\nrestype:container" (without quotes):
那么,你的代码应该做的是将以下公式应用于StringToSign =“GET \\ n \\ n \\ n \\ n \\ n \\ n \\ n \\ n \\ n \\ n \\ n \\ n \\ n-nx-ms-date:Wed, 2015年3月25日22:24:12 GMT \\ nx-ms-version:2014-02-14 \\ n / preconstuff / pings \\ ncomp:list \\ nrestype:container“(不含引号):
Signature=Base64(HMAC-SHA256(AccountKey, UTF8(StringToSign)))
How the service calculates StringToSign is explained in detail in the link shared above. 服务如何计算StringToSign将在上面共享的链接中详细说明。
Worked trough the example code by MrFlick above and to get it to work I had to change a few things. 通过MrFlick上面的示例代码工作并使其工作我不得不改变一些事情。
The date string has to be set in an English locale, for example: 日期字符串必须在英语区域设置中设置,例如:
lct <- Sys.getlocale("LC_TIME")
Sys.setlocale("LC_TIME", "us")
requestdate <- format(Sys.time(),"%a, %d %b %Y %H:%M:%S %Z", tz="GMT")
Sys.setlocale("LC_TIME", lct)
The 'signaturestring' should be formated with \\n between parameters: 应使用参数之间的\\ n格式化'signaturestring':
signaturestring <- paste0("GET", paste(rep("\n", 12), collapse=""),
"x-ms-date:", requestdate,
"\nx-ms-version:2009-09-19\n/preconstuff/pings\ncomp:list\nrestype:container")
EDIT: Following procedure works for me. 编辑:以下程序适合我。 Based on Steph Locke example.
基于Steph Locke的例子。
library(httr)
library(RCurl)
azureBlobCall <- function(url, verb, key, requestBody=NULL, headers=NULL, ifMatch="", md5="") {
urlcomponents <- httr::parse_url(url)
account <- gsub(".blob.core.windows.net", "", urlcomponents$hostname, fixed = TRUE)
container <- urlcomponents$path
# get timestamp in us locale
lct <- Sys.getlocale("LC_TIME"); Sys.setlocale("LC_TIME", "us")
`x-ms-date` <- format(Sys.time(),"%a, %d %b %Y %H:%M:%S %Z", tz="GMT")
Sys.setlocale("LC_TIME", lct)
# if requestBody exist get content length in bytes and content type
`Content-Length` <- ""; `Content-Type` <- ""
if(!is.null(requestBody)) {
if(class(requestBody) == "form_file") {
`Content-Length` <- (file.info(requestBody$path))$size
`Content-Type` <- requestBody$type
} else {
requestBody <- enc2utf8(as.character(requestBody))
`Content-Length` <- nchar(requestBody, "bytes")
`Content-Type` <- "text/plain; charset=UTF-8"
}
}
# combine timestamp and version headers with any input headers, order and create the CanonicalizedHeaders
headers <- setNames(c(`x-ms-date`, "2015-04-05", unlist(headers)),
c("x-ms-date", "x-ms-version", unclass(names(unlist(headers)))))
headers <- headers[order(names(headers))]
CanonicalizedHeaders <- paste(names(headers), headers, sep=":", collapse = "\n")
# create CanonicalizedResource headers and add any queries to it
if(!is.null(urlcomponents$query)) {
components <- setNames(unlist(urlcomponents$query), unclass(names(unlist(urlcomponents$query))))
componentstring <- paste0("\n", paste(names(components[order(names(components))]),
components[order(names(components))], sep=":", collapse = "\n"))
} else componentstring <- ""
CanonicalizedResource <- paste0("/",account,"/",container, componentstring)
# create the authorizationtoken
signaturestring <- paste0(verb, "\n\n\n", `Content-Length`, "\n", md5, "\n", `Content-Type`, "\n\n\n",
ifMatch, "\n\n\n\n", CanonicalizedHeaders, "\n", CanonicalizedResource)
requestspecificencodedkey <- RCurl::base64(
digest::hmac(key=RCurl::base64Decode(key, mode="raw"),
object=enc2utf8(signaturestring),
algo= "sha256", raw=TRUE)
)
authorizationtoken <- paste0("SharedKey ", account, ":", requestspecificencodedkey)
# make the call
headers_final <- add_headers(Authorization=authorizationtoken, headers, `Content-Type` = `Content-Type`)
call <- httr::VERB(verb=verb, url=url, config=headers_final, body=requestBody, verbose())
print("signaturestring");print(signaturestring);
print(headers_final); print(call)
return(content(call))
}
## Tests. Replace 'key' and 'accountName' with yours
key <- "YowThr***********RDw=="
# Creates a container named 'test'
azureBlobCall("https://accountName.blob.core.windows.net/test?restype=container", "PUT", key)
# Creates a blob named 'blob' under container 'test' with the content of "Hej världen!"
azureBlobCall("https://accountName.blob.core.windows.net/test/blob", "PUT", key,
headers = c("x-ms-blob-type"="BlockBlob"), requestBody = "Hej världen!") #upload_file("blob.txt"))
# List all blob in the container 'test'
azureBlobCall("https://accountName.blob.core.windows.net/test?comp=list&restype=container", "GET", key)
# deletes the blobl named 'blob'
azureBlobCall("https://accountName.blob.core.windows.net/test/blob", "DELETE", key)
# Creates a blob named 'blob' under container 'test' with and upload the file 'blob.txt'
azureBlobCall("https://accountName.blob.core.windows.net/test/blob", "PUT", key,
headers = c("x-ms-blob-type"="BlockBlob"), requestBody = upload_file("blob.txt"))
# deletes the container named 'test'
azureBlobCall("https://accountName.blob.core.windows.net/test?restype=container", "DELETE", key)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.