简体   繁体   English

从R连接到azure blob存储API时出错

[英]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

Setup 建立

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() ))

Error 错误

Top level message 顶级消息

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签名与任何计算签名不同。

Response content 回复内容

[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>"

Verbose output 详细输出

-> 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
<- 

Resolving the error 解决错误

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: 为此我检查了:

  1. I've verified my key is correct (it's just c&p from portal) 我已经验证了我的密钥是正确的(它只是门户网站的c&p)
  2. I've made sure the date is correctly formatted 我确保日期格式正确
  3. There was a recent documentDB SO that suggested it could be to be a clock skew issue and I do note that my x-ms-date is a second ahead of the Date in the response. 有一个最近的documentDB SO表明它可能是一个时钟偏差问题,我注意到我的x-ms-date比响应中的Date早了一秒。 I've tried sending a fixed value that was definitely in the past, but within the 15 mins tolerance. 我已经尝试发送一个绝对过去的固定值,但是在15分钟的容差范围内。 Didn't get a change in message. 没有得到消息的改变。
  4. Added encoding="Base64" in headerstuff further to an MSDN forum question but the same error message was returned headerstuff添加了 encoding="Base64" ,进一步 headerstuffMSDN论坛问题,但返回了相同的错误消息
  5. Further to @Serdar's answer, I incorporated the construction of a signature string (I've verified that this matches the one provided in the error message so it), then encoding in base64 a hmac-sha256 (using the secondary access key ( sak ) as the encryption key) version of the UTF8 converted signaturestring as the value to be used in the SharedKey authorisation. 继@ Serdar的回答,我结合了签名字符串的构造(我已经验证这与错误消息中提供的字符串相匹配),然后在base64中编码hmac-sha256(使用辅助访问密钥( sak ))作为加密密钥)UTF8转换的signaturestring字符串的版本作为要在SharedKey授权中使用的值。
  6. Further to @Serdar's comment, the date used in the signature string and for the main request must be the same, so defined once and reused 继@ Serdar的评论之后,签名字符串和主要请求中使用的日期必须相同,因此定义一次并重复使用

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM