Java Azure请求签名

我正在使用最新的Azure SDK存储模拟器。 我正在尝试为我的blob签名。 当我运行以下代码时,我收到auth错误。

虽然我已多次检查过代码是否符合Azure SDK blob访问规范,但我无法弄清楚出了什么问题。

这是控制台输出:

GET x-ms-date:Sun, 23 Sep 2012 04:04:07 GMT /devstoreaccount1/tweet/?comp=list SharedKey devstoreaccount1:Hx3Pm9knGwCb4Hs9ftBX/+QlX0kCGGlUOX5g6JHZ9Kw= Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature. 

这是代码:

 public static void signRequest(HttpURLConnection request, String account, String key) throws Exception { SimpleDateFormat fmt = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss"); fmt.setTimeZone(TimeZone.getTimeZone("GMT")); String date = fmt.format(Calendar.getInstance().getTime()) + " GMT"; StringBuilder sb = new StringBuilder(); sb.append("GET\n"); // method sb.append('\n'); // md5 (optional) sb.append('\n'); // content type sb.append('\n'); // legacy date sb.append("x-ms-date:" + date + '\n'); // headers sb.append(request.getURL().getPath() + "/tweet/?comp=list"); // resource TODO: "?comp=..." if present System.out.println(sb.toString()); Mac mac = Mac.getInstance("HmacSHA256"); mac.init(new SecretKeySpec(Base64.decode(key), "HmacSHA256")); String authKey = new String(Base64.encode(mac.doFinal(sb.toString().getBytes("UTF-8")))); String auth = "SharedKey " + account + ":" + authKey; request.setRequestProperty("x-ms-date", date); request.setRequestProperty("Authorization", auth); request.setRequestMethod("GET"); System.out.println(auth); } public static void main(String args[]) throws Exception { String account = "devstoreaccount1"; String key = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw=="; HttpURLConnection connection = (HttpURLConnection) (new URL("http://localhost:10000/devstoreaccount1")).openConnection(); signRequest(connection, account, key); connection.connect(); System.out.println(connection.getResponseMessage()); } 

在Gaurav和Smarx的反馈之后,这里是代码,我仍然得到同样的错误。 你能告诉我一些代码吗? 否则很难理解。

  public static void sign(HttpURLConnection request, String account, String key, String url) throws Exception { SimpleDateFormat fmt = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss"); fmt.setTimeZone(TimeZone.getTimeZone("GMT")); String date = fmt.format(Calendar.getInstance().getTime()) + " GMT"; StringBuilder sb = new StringBuilder(); sb.append("GET\n"); // method sb.append('\n'); // md5 (optional) sb.append('\n'); // content type sb.append('\n'); // legacy date sb.append("x-ms-date:" + date + '\n'); // headers sb.append("x-ms-version:2009-09-19\n"); // headers sb.append("/devstoreaccount1/devstoreaccount1/\n$maxresults:1\ncomp:list\nrestype:container"); // resource TODO: "?comp=..." if present System.out.println(sb.toString()); Mac mac = Mac.getInstance("HmacSHA256"); mac.init(new SecretKeySpec(Base64.decode(key), "HmacSHA256")); String authKey = new String(Base64.encode(mac.doFinal(sb.toString().getBytes("UTF-8")))); String auth = "SharedKeyLite " + account + ":" + authKey; request.setRequestProperty("x-ms-date", date); request.setRequestProperty("Authorization", auth); request.setRequestMethod("GET"); System.out.println(auth); } public static void main(String args[]) throws Exception { String account = "devstoreaccount1"; String key = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw=="; String url = "http://127.0.0.1:10000/devstoreaccount1/?restype=container&comp=list&$maxresults=1"; HttpURLConnection connection = (HttpURLConnection) (new URL(url)).openConnection(); sign(connection, account, key, url); connection.connect(); System.out.println(connection.getResponseMessage()); } 

编辑 Gaurav的答案在哪里? :-)我相信他已经回答并提到您似乎正在构建Shared Key Lite签名,因此应在您的授权标头中使用“SharedKeyLite”。


我认为Gaurav的答案是正确的,但我注意到其他三个问题:

  1. 您似乎正在调用http://localhost/devstoreaccount1 ,但您正在计算http://localhost/devstoreaccount1/tweet/?comp=list的签名。 确保url匹配。
  2. 对于存储模拟器,我认为您的规范化资源实际上应该是/devstoreaccount1/devstoreaccount1/tweet/?comp=list 。 (请注意重复帐户名。)通常应为// ,对于存储模拟器,帐户名称将显示在路径中。
  3. 哪个是x-ms-version标头? 我相信这是必需的。

更新这里有一些使用两种方法的工作代码,一种使用共享密钥,另一种使用Shared Key Lite。 希望这可以解决问题。 请注意,要使用存储模拟器,您需要将URL切换回localhost:10000/devstoreaccount1 。 签名代码仍应适用于模拟器,但我还没有测试过。 Base64库来自这里: http : //commons.apache.org/codec/apidocs/org/apache/commons/codec/binary/Base64.html 。

 import java.net.*; import java.util.*; import java.text.*; import javax.crypto.*; import javax.crypto.spec.*; import org.apache.commons.codec.binary.Base64; public class Test { private static Base64 base64 = new Base64(); public static void signRequestSK(HttpURLConnection request, String account, String key) throws Exception { SimpleDateFormat fmt = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss"); fmt.setTimeZone(TimeZone.getTimeZone("GMT")); String date = fmt.format(Calendar.getInstance().getTime()) + " GMT"; StringBuilder sb = new StringBuilder(); sb.append("GET\n"); // method sb.append('\n'); // content encoding sb.append('\n'); // content language sb.append('\n'); // content length sb.append('\n'); // md5 (optional) sb.append('\n'); // content type sb.append('\n'); // legacy date sb.append('\n'); // if-modified-since sb.append('\n'); // if-match sb.append('\n'); // if-none-match sb.append('\n'); // if-unmodified-since sb.append('\n'); // range sb.append("x-ms-date:" + date + '\n'); // headers sb.append("x-ms-version:2009-09-19\n"); sb.append("/" + account + request.getURL().getPath() + "\ncomp:list"); //System.out.println(sb.toString()); Mac mac = Mac.getInstance("HmacSHA256"); mac.init(new SecretKeySpec(base64.decode(key), "HmacSHA256")); String authKey = new String(base64.encode(mac.doFinal(sb.toString().getBytes("UTF-8")))); String auth = "SharedKey " + account + ":" + authKey; request.setRequestProperty("x-ms-date", date); request.setRequestProperty("x-ms-version", "2009-09-19"); request.setRequestProperty("Authorization", auth); request.setRequestMethod("GET"); System.out.println(auth); } public static void signRequestSKL(HttpURLConnection request, String account, String key) throws Exception { SimpleDateFormat fmt = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss"); fmt.setTimeZone(TimeZone.getTimeZone("GMT")); String date = fmt.format(Calendar.getInstance().getTime()) + " GMT"; StringBuilder sb = new StringBuilder(); sb.append("GET\n"); // method sb.append('\n'); // md5 (optional) sb.append('\n'); // content type sb.append('\n'); // legacy date sb.append("x-ms-date:" + date + '\n'); // headers sb.append("x-ms-version:2009-09-19\n"); sb.append("/" + account + request.getURL().getPath() + "?comp=list"); //System.out.println(sb.toString()); Mac mac = Mac.getInstance("HmacSHA256"); mac.init(new SecretKeySpec(base64.decode(key), "HmacSHA256")); String authKey = new String(base64.encode(mac.doFinal(sb.toString().getBytes("UTF-8")))); String auth = "SharedKeyLite " + account + ":" + authKey; request.setRequestProperty("x-ms-date", date); request.setRequestProperty("x-ms-version", "2009-09-19"); request.setRequestProperty("Authorization", auth); request.setRequestMethod("GET"); System.out.println(auth); } public static void main(String args[]) throws Exception { String account = args[0]; String key = args[1]; HttpURLConnection connection = (HttpURLConnection) (new URL("http://" + account + ".blob.core.windows.net/?comp=list")).openConnection(); signRequestSKL(connection, account, key); connection.connect(); System.out.println(connection.getResponseMessage()); connection = (HttpURLConnection) (new URL("http://" + account + ".blob.core.windows.net/?comp=list")).openConnection(); signRequestSK(connection, account, key); connection.connect(); System.out.println(connection.getResponseMessage()); } } 

基于此处的文档: http : //msdn.microsoft.com/en-us/library/windowsazure/dd179428 ,我相信您构建“Canonicalized Resource String”部分签名的方式存在问题。

我注意到的一些事情:

  • 您将查询字符串参数(comp = list)附加到此字符串,您不应该这样做。
  • 如果你正在针对开发存储(你正在做)构建这个字符串,那么“devstoreaccount1”应该会出现两次。

例如,如果我尝试在我的开发存储帐户中仅列出1个blob容器,则应该是基于以下请求URL的canonocalized资源字符串 – 127.0.0.1:10000/devstoreaccount1/?restype=container&comp=list&$maxresults= 1:

/ devstoreaccount1 / devstoreaccount1 /

$的maxResults:1

比较:列表

restype:容器