【问题标题】:Android post request to django server using HttpUrlConnection gives 403Android使用HttpUrlConnection向django服务器发布请求给出403
【发布时间】:2015-09-20 17:16:13
【问题描述】:

我正在使用 django 服务器在 android 中学习网络。我能够发出 GET 请求并获得 json 响应以及我能够获得 csrf cookie,但是当我使用 cookie 并发出 post 请求时,django 给出 403 表示 csrf 验证失败。

我正在使用 csrf 令牌设置所需的标头“X-CSRFToken”,并且还传递了 cookie (cookieString),但到目前为止还没有成功。

这是我在 android 中的课程。我可以使用 python-requests 库发出发布请求,因此 django 服务器没有问题,这个 android 代码中有一些问题。请提出任何解决此问题的方法。

private void signupUser() {
    validateData();
    new PostData().execute();
}

private void validateData() {
    try {
        urlParameters= URLEncoder.encode("code","utf-8")+"="+URLEncoder.encode("100","utf-8");
        urlParameters+= "&"+URLEncoder.encode("username","utf-8")+"="+URLEncoder.encode(username,"utf-8");
        urlParameters+= "&"+URLEncoder.encode("password","utf-8")+"="+URLEncoder.encode(password,"utf-8");
        urlParameters+= "&"+URLEncoder.encode("firstName","utf-8")+"="+URLEncoder.encode(firstName,"utf-8");
        urlParameters+= "&"+URLEncoder.encode("lastName","utf-8")+"="+URLEncoder.encode(lastName,"utf-8");
        urlParameters+= "&"+URLEncoder.encode("email","utf-8")+"="+URLEncoder.encode(email,"utf-8");
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    }
}

private class PostData extends AsyncTask<Void,Void,String> {

    @Override
    protected String doInBackground(Void... params) {

        String response="";

        try {

            // Manage Cookies
            String cookieString="";
            String csrftoken="";


            cookieManager=io.getCookiesFromURLConnection(urlConnection);
            List<HttpCookie> cookies=cookieManager.getCookieStore().getCookies();
            Iterator<HttpCookie> cookieIterator=cookies.iterator();
            while(cookieIterator.hasNext()){
                HttpCookie cookie=cookieIterator.next();
                cookieString+=cookie.getName()+"="+cookie.getValue()+";";

                if(cookie.getName().equals("csrftoken")){
                    csrftoken=cookie.getValue();
                }
            }


            url= new URL(Utils.USER_SIGNUP_URL);
            urlConnection=(HttpURLConnection)url.openConnection();
            urlConnection.setRequestMethod("POST");
            urlConnection.setRequestProperty("User-Agent", Utils.USER_AGENT);
            urlConnection.setRequestProperty("Connection", Utils.KEEP_ALIVE);
            urlConnection.setDoOutput(true);
            urlConnection.setDoInput(true);

            urlConnection.setRequestProperty("X-CSRFToken", csrftoken);
            urlConnection.setRequestProperty("Cookies", cookieString);

            OutputStreamWriter streamWriter=new OutputStreamWriter(urlConnection.getOutputStream());

            streamWriter.write(urlParameters);
            streamWriter.flush();
            streamWriter.close();
            int responseCode=urlConnection.getResponseCode();
            response=io.readStream(urlConnection);

        } catch (FileNotFoundException ex){
            ex.printStackTrace();
            try {
                response=io.readErrorStream(urlConnection);
            } catch (IOException e) {
                e.printStackTrace();
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
        return response;
    }
}

private class GetData extends AsyncTask<Void,Void,String> {

    @Override
    protected String doInBackground(Void... params) {
        if(checkConnection()){
            try {
                return fetchData();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }else{
            Toast.makeText(SignUpActivity.this, "Please connect to internet", Toast.LENGTH_LONG).show();
        }
        return "";
    }

    @Override
    protected void onPostExecute(String s) {
        if(s==null){
            return ;
        }
        super.onPostExecute(s);
        signupButton.setEnabled(true);
    }

}

private boolean checkConnection() {
    ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
    String result = "";
    if (networkInfo != null && networkInfo.isConnected()) {
        return true;
    }
    return false;
}

public  String fetchData() throws IOException {

    String result="";

    try {
        url= new URL(Utils.USER_SIGNUP_URL);
        urlConnection=(HttpURLConnection)url.openConnection();
        result=io.readStream(urlConnection);
        cookieManager=io.getCookiesFromURLConnection(urlConnection);
    } catch (MalformedURLException e) {
        e.printStackTrace();
    }

    return result;

}

}

这里是 io 类: 公共类 io {

public static CookieManager getCookiesFromURLConnection(HttpURLConnection urlConnection){
    Map<String,List<String >> headers= urlConnection.getHeaderFields();
    CookieManager cookieManager=new CookieManager();
    List<String> cookiesHeader=headers.get("Set-Cookie");

    if(cookiesHeader!=null){
        for(String cookie: cookiesHeader){
            cookieManager.getCookieStore().add(null, HttpCookie.parse(cookie).get(0));
        }
    }

    return cookieManager;
}

}

Android Logcat:

09-19 20:55:37.479  17085-17734/kam.app.learnnetworking W/System.err﹕ java.io.FileNotFoundException: http://<website-ip-address>/api/user-login/
09-19 20:55:37.484  17085-17734/kam.app.learnnetworking W/System.err﹕ at com.android.okhttp.internal.http.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:206)
09-19 20:55:37.484  17085-17734/kam.app.learnnetworking W/System.err﹕ at kam.app.learnnetworking.networking.io.readStream(io.java:28)
09-19 20:55:37.484  17085-17734/kam.app.learnnetworking W/System.err﹕ at kam.app.learnnetworking.activities.SignUpActivity$PostData.doInBackground(SignUpActivity.java:172)
09-19 20:55:37.484  17085-17734/kam.app.learnnetworking W/System.err﹕ at kam.app.learnnetworking.activities.SignUpActivity$PostData.doInBackground(SignUpActivity.java:123)
09-19 20:55:37.484  17085-17734/kam.app.learnnetworking W/System.err﹕ at android.os.AsyncTask$2.call(AsyncTask.java:292)
09-19 20:55:37.484  17085-17734/kam.app.learnnetworking W/System.err﹕ at java.util.concurrent.FutureTask.run(FutureTask.java:237)
09-19 20:55:37.484  17085-17734/kam.app.learnnetworking W/System.err﹕ at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
09-19 20:55:37.484  17085-17734/kam.app.learnnetworking W/System.err﹕ at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
09-19 20:55:37.484  17085-17734/kam.app.learnnetworking W/System.err﹕ at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
09-19 20:55:37.484  17085-17734/kam.app.learnnetworking W/System.err﹕ at java.lang.Thread.run(Thread.java:818)

有什么方法可以获取发送的请求并在 android 日志中打印?我是 android 网络新手,所以不太了解。

【问题讨论】:

  • 请修剪您的问题以仅包含相关代码,而不是仅仅转储整个项目,更有可能让人们以这种方式阅读它!
  • 我已经编辑了这个问题,但没有必要为此投反对票。
  • logcat 中的第一个错误行:09-19 20:55:37.479 17085-17734/kam.app.learnnetworking W/System.err: java.io.FileNotFoundException: http:///api/user-login/
  • 即检查HttpUrlconnection var的值
  • @Fred 谢谢,但它为响应 403 提供了 filenotfound。当我使用 readErrorStream 时,它为我提供了 csrf 失败响应的 html。

标签: android python django cookies


【解决方案1】:

问题是 android 项目和 django 项目可能在不同的服务器上。因此,请求因 CORS 而失败。例如,如果你在 django api 中使用 sweetpie,那么你必须像这样设置 CORS:

class CORSResource(object):
"""
Adds CORS headers to resources that subclass this.
"""
def create_response(self, *args, **kwargs):
    response = super(CORSResource, self).create_response(*args, **kwargs)
    response['Access-Control-Allow-Origin'] = '*'
    response['Access-Control-Allow-Headers'] = 'Content-Type'
    return response

def method_check(self, request, allowed=None):
    if allowed is None:
        allowed = []

    request_method = request.method.lower()
    allows = ','.join(map(str.upper, allowed))

    if request_method == 'options':
        response = HttpResponse(allows)
        response['Access-Control-Allow-Origin'] = '*'
        response['Access-Control-Allow-Headers'] = 'Content-Type'
        response['Allow'] = allows
        raise ImmediateHttpResponse(response=response)

    if not request_method in allowed:
        response = http.HttpMethodNotAllowed(allows)
        response['Allow'] = allows
        raise ImmediateHttpResponse(response=response)

您必须将 CORS 标头添加到任何跨域的内容中。您可以根据框架和要允许的方法对其进行自定义。

【讨论】:

  • 嗨瓦特萨尔。我没有为 api 使用任何第三方应用程序。我只是发送 JsonResponse。你能告诉我如何启用CORS吗?
  • 谢谢。但是要放入“CORS_ORIGIN_WHITELIST”的网址,因为我使用的是android应用程序。我不想通过允许所有人来妥协安全性。
  • @KamalSingh 在你的情况下。你必须使用这个 CORS_ORIGIN_ALLOW_ALL = True。并将其他保留为默认值
  • 谢谢Vatsal,我会试试看它是否有效。但这不会是一个安全问题吗?
  • 是的,它确实增加了风险。默认情况下不允许它的原因之一。但是,可以通过使用 API 密钥、oauth 等身份验证来降低风险。
【解决方案2】:

终于解决了。这是一个非常愚蠢的错误。

而不是:

urlConnection.setRequestProperty("Cookies", cookieString);

应该是:

urlConnection.setRequestProperty("Cookie", cookieString);

【讨论】:

    猜你喜欢
    • 2016-10-26
    • 1970-01-01
    • 2019-08-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-27
    • 2012-05-23
    相关资源
    最近更新 更多