我正在编写一个Android应用程序,它以JSON格式将数据发送到在本地服务器上运行的Django REST API。它是与服务器的https连接,所有必要的证书都集成到应用程序中。 Bevore我们已经更新到Django 1.6,我们在Django 1.4上运行,一切正常。现在使用Django 1.6,应用程序仍然能够获取CSRF令牌,登录并执行HttpGet请求,该请求返回预期的JSON对象。 403 Forbidden仅拒绝HttpPost请求。我在网上搜索并发现Cross Site Request Forgery保护可能是一个问题,但是我们通过webbrowser和app访问API,所以我认为安全缺乏将其关闭。
以下是我启动httpPost和HttpGet的方法:
启动应用程序后,会创建一个包含服务器证书的HttpClient,这个用于所有get和post请求:
private DefaultHttpClient createHttpClient(Context ctx) throws CertificateException, IOException, KeyStoreException, NoSuchAlgorithmException, KeyManagementException, UnrecoverableKeyException
{
SSLSocketFactory ssf = new SSLSocketFactory(keyStore);
SchemeRegistry schemeRegistry = new SchemeRegistry();
// http scheme
schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 888));
// https scheme
schemeRegistry.register(new Scheme("https", ssf, 443));
HttpParams params = new BasicHttpParams();
params.setParameter(HttpProtocolParams.USE_EXPECT_CONTINUE, true);
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(params, "utf-8");
//ClientConnectionManager cm =
// new ThreadSafeClientConnManager(params, schemeRegistry);
ClientConnectionManager cm =
new SingleClientConnManager(params, schemeRegistry);
return new DefaultHttpClient(cm, params);
}
然后我发送获取请求以获取CSRF令牌并在之后登录:
public static String openAuthStream(DefaultHttpClient httpClient, String u, String pw) throws ClientProtocolException, IOException
{
// defaultHttpClient
HttpGet httpGet = new HttpGet(Constants.LOGIN_URL);
httpClient.execute(httpGet);
String csrfToken = new String();
CookieStore cookieStore = httpClient.getCookieStore();
List <Cookie> cookies = cookieStore.getCookies();
for (Cookie cookie: cookies) {
if (cookie.getDomain().equals(Constants.CSRF_DOMAIN) && cookie.getName().equals("csrftoken")) {
csrfToken = cookie.getValue();
}
}
httpGet.setHeader("Referer", Constants.LOGIN_URL);
httpGet.setHeader("X-CSRFToken", csrfToken);
//httpGet.abort();
List<NameValuePair> credentials = new ArrayList<NameValuePair>();
credentials.add(new BasicNameValuePair("username", u));
credentials.add(new BasicNameValuePair("password", pw));
HttpPost post = new HttpPost(Constants.LOGIN_URL);
post.setHeader("Referer", Constants.LOGIN_URL);
post.setHeader("X-CSRFToken", csrfToken);
post.setEntity(new UrlEncodedFormEntity(credentials));
HttpResponse response = httpClient.execute(post);
//post.abort();
if(response.getStatusLine().getStatusCode() == 200)
return csrfToken;
return null;
}
最后,帖子请求以这种方式建立:
public HttpResponse sendJSONToUrl(DefaultHttpClient httpClient,String url, String csrfToken, JSONObject json) {
try {
HttpPost httpPostJson = new HttpPost(url);
httpPostJson.setEntity(new StringEntity(json.toString(),HTTP.UTF_8));
httpPostJson.setHeader("Referer", url);
httpPostJson.setHeader("X-CSRFToken", csrfToken);
httpPostJson.setHeader("Accept", "application/json");
httpPostJson.setHeader("Content-type", "application/json");
HttpResponse response = httpClient.execute(httpPostJson);
httpPostJson.abort();
return response;
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
在Django 1.4上一切正常,现在我很无奈。 提前谢谢!
答案 0 :(得分:0)
这不是跨站点请求伪造保护问题。您应该为POST请求传递正确的ContentType标头。 Django在版本1.5之前没有验证它,但是从1.5开始就检查了它 - http://django.readthedocs.org/en/latest/releases/1.5.html#non-form-data-in-http-requests。
因此,您需要更改请求的ContentType标头:
httpPostJson.setHeader("Content-Type", "application/x-www-form-urlencoded");
答案 1 :(得分:0)
解决了这个问题。成功登录后,CSRF令牌会发生变化(Django 1.4不会发生这种情况),因此在使用登录信息进行POST后,必须另外进行GET以获取新的CSRF令牌,然后一切正常。感谢所有的帮助!