Android向Django服务器发送POST请求时,CSRF验证失败。

10
我希望我的Android应用程序能够向我的Django服务器发送一些信息。因此,我让Android应用程序将一个POST请求发送到mysite/upload页面,Django的视图将根据POST数据进行处理。问题是,服务器针对POST请求给出的响应抱怨CSRF验证失败。调查这个问题,似乎我可能需要先从服务器获取一个CSRF令牌,然后使用该令牌进行POST。但我不确定如何做到这一点。编辑:我已经发现可以使用视图装饰器@csrf_exempt关闭此视图的CSRF验证,但我不确定这是否是最佳解决方案。我的Android代码:
// Create a new HttpClient and Post Header
                    HttpClient httpclient = new DefaultHttpClient();
                    HttpPost httppost = new HttpPost(URL);

                    // Add your data
                    List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);
                    nameValuePairs.add(new BasicNameValuePair("scoreone", scoreone));
                    nameValuePairs.add(new BasicNameValuePair("scoretwo", scoretwo));
                    httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
                    System.out.println("huzahhhhhhh");
                    // Execute HTTP Post Request
                    HttpResponse response = httpclient.execute(httppost);
                    BufferedReader in = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
                    StringBuffer sb = new StringBuffer("");
                    String line = "";
                    String NL = System.getProperty("line.separator");
                    while ((line = in.readLine()) != null) {
                        sb.append(line + NL);
                    }
                    in.close();
                    String result = sb.toString();
                    System.out.println("Result: "+result);

这是我的视图处理上传的代码:

# uploads a players match
def upload(request):
    if request.method == 'POST':
        scoreone = int(request.POST['scoreone'])
        scoretwo = int(request.POST['scoretwo'])
        m = Match.objects.create()
        MatchParticipant.objects.create(player = Player.objects.get(pk=1), match = m, score = scoreone)
        MatchParticipant.objects.create(player = Player.objects.get(pk=2), match = m, score = scoretwo)
    return HttpResponse("Match uploaded" )

enter code here

我遇到了403错误,我正在做同样的事情。如果您已经解决了这个错误,能否帮助我? - Harshit Gupta
3个回答

10

首先,您需要从预览请求的cookies中读取csrf令牌:

httpClient.execute(new HttpGet(uri));
CookieStore cookieStore = httpClient.getCookieStore();
List <Cookie> cookies =  cookieStore.getCookies();
for (Cookie cookie: cookies) {
    if (cookie.getDomain().equals(Constants.CSRF_COOKIE_DOMAIN) && cookie.getName().equals("csrftoken")) {
        CSRFTOKEN = cookie.getValue();
    }
}

如果您的视图未呈现包含csrf_token模板标记的模板,则Django可能不会设置CSRF令牌cookie。在动态添加表单的情况下,这种情况很常见。为了解决这个问题,Django提供了一个视图装饰器来强制设置cookie:ensure_csrf_cookie()。-- https://docs.djangoproject.com/en/dev/ref/contrib/csrf/#ajax 然后,在进行POST请求时,您可以将其传递到服务器上的请求头和cookie中:
httpPost.setHeader("Referer", Constants.SITE_URL);
httpPost.setHeader("X-CSRFToken", CSRFTOKEN);

DefaultHttpClient client = new DefaultHttpClient();
final BasicCookieStore cookieStore =  new BasicCookieStore();

BasicClientCookie csrf_cookie = new BasicClientCookie("csrftoken", CSRFTOKEN);
csrf_cookie.setDomain(Constants.CSRF_COOKIE_DOMAIN);
cookieStore.addCookie(csrf_cookie);

// Create local HTTP context - to store cookies
HttpContext localContext = new BasicHttpContext();
// Bind custom cookie store to the local context
localContext.setAttribute(ClientContext.COOKIE_STORE, cookieStore);

HttpResponse response = client.execute(httpPost, localContext);

我正在做完全相同的事情,但是我得到了403错误。 我已经尝试发送cookies、headers,但仍然得到相同的错误。 - Harshit Gupta

2

编写自己的装饰器并向请求添加一些“秘密”标头。 https://code.djangoproject.com/browser/django/trunk/django/views/decorators/csrf.py

def csrf_exempt(view_func):
        """
        Marks a view function as being exempt from the CSRF view protection.
        """
        # We could just do view_func.csrf_exempt = True, but decorators
        # are nicer if they don't have side-effects, so we return a new
        # function.
        def wrapped_view(request,*args, **kwargs):
            return view_func(request, *args, **kwargs)
            if request.META.has_key('HTTP_X_SKIP_CSRF'):
                wrapped_view.csrf_exempt = True
        return wraps(view_func, assigned=available_attrs(view_func))(wrapped_view)

4
你正在绕过安全措施。 - remus
1
@remus Android应用程序的CSRF没有意义。 跨站请求伪造是针对传统网页的,对于API来说是无意义的。 - baklarz2048

2

关闭CSRF验证确实有效!但您确定要这样做吗?您最初的想法是:从服务器获取令牌,并将其与POST数据一起发送,这样做要好得多。

CSRF令牌通常以cookie形式存在。例如,在Django框架中,您有一个名为csrftoken的cookie,您需要获取该值并将其作为“X-CSRFToken”发布到服务器。


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