HttpURLConnection向Google Cloud Storage进行PUT操作时出现403错误

12

我尝试使用XML API将文件上传到Google Cloud Storage。每次上传时,我都针对其生成了正确的GoogleAccessId、到期日期和签名。奇怪的是,我可以使用Chrome浏览器的Postman应用程序PUT文件,因此我确定URL没有问题。但我无法在我的Android Java程序中PUT文件(它返回403错误)。执行上传的源代码在这里(基于此代码:https://cloud.google.com/storage/docs/access-control#Signing-Strings):

    URL url;
    HttpURLConnection connection;

    try {
        url = new URL("http://google-testbucket.storage.googleapis.com/testdata.txt?GoogleAccessId=1234567890123@developer.gserviceaccount.com&Expires=1331155464&Signature=BClz9e4UA2MRRDX62TPd8sNpUCxVsqUDG3YGPWvPcwN%2BmWBPqwgUYcOSszCPlgWREeF7oPGowkeKk7J4WApzkzxERdOQmAdrvshKSzUHg8Jqp1lw9tbiJfE2ExdOOIoJVmGLoDeAGnfzCd4fTsWcLbal9sFpqXsQI8IQi1493mw%3D");
        connection = (HttpURLConnection) url.openConnection();
        connection.setDoOutput(true);
        connection.setRequestMethod("PUT");

        OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream());
        out.write("Test");
        out.close();

        Log.i("TAG", "PUT Response code: " + connection.getResponseCode());

    } catch (MalformedURLException e) {
        e.printStackTrace();
        Log.e("TAG", "MalformedURLException");
    } catch (ProtocolException e) {
        e.printStackTrace();
        Log.e("TAG", "ProtocolException");
    } catch (IOException e) {
        e.printStackTrace();
        Log.e("TAG", "IOException");
    }

PUT Object的文档: https://cloud.google.com/storage/docs/xml-api/put-object-upload

有人可以研究一下这个问题,并给我提供一些可能出错的提示吗?


可能是重复的问题,与此类似:http://stackoverflow.com/questions/17225638/forbiddenerror-when-attempting-to-write-file-to-gcs-from-gae-python-app。很可能是因为您的Android应用程序没有得到适当的身份验证,而在Chrome中您已经登录了(我假设您已经在Chrome中登录了您的Google帐户)。 - morpheus05
我认为这不是一个关键点,因为对于Postman而言,即使我从任何一个账户中退出登录,它也能正常工作。必须有不同的根本原因,但我已经没有更多的想法了。 - mysliwiec_tech
如果您使用签名URL,则URL可能会被错误地签名。 - morpheus05
我几乎肯定不是这种情况,因为相同的参数(包括签名URL)在Postman中运行正常,在我的Android应用程序中无法工作。 - mysliwiec_tech
1
我认为这个链接与你的问题有些共同之处 https://dev59.com/omIj5IYBdhLWcg3wx34x - youngdero
感谢你的评论 @youngdero。简单来说,我可以说这是解决我的问题的方法:https://dev59.com/omIj5IYBdhLWcg3wx34x#25118102。我在请求中自动添加了Content-Type头部,因此它必须出现在签名字符串中。 - mysliwiec_tech
2个回答

11

我刚刚发现HttpURLConnection会自动添加一个值为application/x-www-form-urlencodedContent-Type头部。我在我的Android模拟器上使用HTTP抓包工具进行了测试。

这个自动添加的头部导致了签名不匹配的问题。当我改变了服务器端的代码,允许带有Content-Type: application/x-www-form-urlencoded的请求时,它生成了正确的签名并且正常工作。

感谢 @morpheus05 的承诺。


1
请设置您的 Content-Type 如下所示。
connection.setRequestProperty("Content-Type"," ");

因为 HttpsUrlConnection 会自动生成 Content-Type。
"Content-Type: application/x-www-form-urlencoded"

这将导致签名不匹配。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接