Django更新到1.6后,Android http post请求返回403

时间:2014-07-24 07:38:26

标签: java android django http-post django-csrf

我正在编写一个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上一切正常,现在我很无奈。 提前谢谢!

2 个答案:

答案 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令牌,然后一切正常。感谢所有的帮助!