0

我正在为我组织的新网站开发 Android 客户端 API。新网站是用 Rails 编写的,身份验证按如下方式完成:我使用凭据向网站的登录 URL 发出 POST 请求,并向 Android 的 cookie 管理器返回一个 CSRF 令牌和一个 cookie。然后我将 CSRF 令牌传递给经过身份验证的函数。

我的问题是我正在尝试实现的 UploadCSV 函数。当您单击“上传 CSV”按钮时,我模仿了网站 UI 生成的请求,但该网站拒绝了我的请求并告诉我它无法验证我的 CSRF 令牌。同样的令牌也适用于其他经过身份验证的功能,我终生无法弄清楚原因。

这是我创建请求的函数:

URI url = new URI(baseURL+"/projects/"+projectId+"/CSVUpload");
HttpClient client = new DefaultHttpClient();
HttpPost httpPost = new HttpPost(url);
MultipartEntity entity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE,null,Charset.forName(HTTP.UTF_8));
entity.addPart("authenticity_token", new StringBody(authToken));
entity.addPart("csv", new FileBody(csvToUpload));
entity.addPart("utf8", new StringBody("\u2713", "text/plain", Charset.forName("UTF-8")));
httpPost.setEntity(entity);
client.execute(httpPost);

这是服务器日志消息:

Started POST "/projects/7/CSVUpload" for ***.**.***.** at 2013-08-13 08:38:13 -0400
Processing by DataSetsController#uploadCSV as HTML
  Parameters: {"authenticity_token"=>"OvJanht9/GZu90rcRrR3YH94M8Ukamr5OLp/qiOEZAY=", "csv"=>#<ActionDispatch::Http::UploadedFile:0x007fd6ae12a7a0 @original_filename="weird.csv", @content_type="application/octet-stream", @headers="Content-Disposition: form-data; name=\"csv\"; filename=\"weird.csv\"\r\nContent-Type: application/octet-stream\r\n", @tempfile=#<File:/var/folders/9q/v5l53m3141d4cbrd8w07g0sm0000gn/T/RackMultipart20130813-55943-5v1kp8>>, "utf8"=>"✓", "id"=>"7"}
WARNING: Can't verify CSRF token authenticity
  User Load (0.2ms)  SELECT "users".* FROM "users" WHERE "users"."id" IS NULL LIMIT 1
  CACHE (0.0ms)  SELECT "users".* FROM "users" WHERE "users"."id" IS NULL LIMIT 1
Redirected to http://***.**.**.**:3000/
Filter chain halted as :authorize rendered or redirected
Completed 302 Found in 12ms (ActiveRecord: 0.3ms)

我的 API 中的其他函数使用 HTTPUrlConnection 方法来发送他们的 POST 请求,并且这些验证很好。我想使用 HttpPost 发送 MultipartEntity。

4

2 回答 2

0

您是否尝试在标头中发送令牌X-CSRF-Token?我猜你的一些请求没有抱怨的原因是因为他们使用GET,它跳过了验证。

如果你也想绕过它POST,你可以添加 a skip_before_filter :verify_authenticity_token,尽管我提到这一点并警告不推荐这样做。

于 2013-08-13T15:38:42.200 回答
0

显然答案是不使用 HttpPost。我将请求更改为使用 HttpURLConnection 并且它成功了。

URL url = new URL(baseURL+"/projects/"+projectId+"/CSVUpload?authenticity_token="+URLEncoder.encode(authToken, "UTF-8"));
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoOutput(true);
connection.setRequestMethod("POST");

MultipartEntity entity = new MultipartEntity();
entity.addPart("utf8", new StringBody("\u2713", "text/plain", Charset.forName("UTF-8")));
entity.addPart("csv", new FileBody(csvToUpload, "text/csv"));

connection.setRequestProperty("Content-Type", entity.getContentType().getValue());
OutputStream out = connection.getOutputStream();
try {
    entity.writeTo(out);
} finally {
    out.close();
}
connection.getResponseCode();

它有点不简洁,但它确实有效。

于 2013-08-19T15:02:58.813 回答