GVKun编程网logo

Android 8:不允许明文 HTTP 流量(安卓11不允许访问data)

20

本文将为您提供关于Android8:不允许明文HTTP流量的详细介绍,我们还将为您解释安卓11不允许访问data的相关知识,同时,我们还将为您提供关于Android8:不允许使用CleartextHT

本文将为您提供关于Android 8:不允许明文 HTTP 流量的详细介绍,我们还将为您解释安卓11不允许访问data的相关知识,同时,我们还将为您提供关于Android 8:不允许使用Cleartext HTTP流量、android httpClient (https/http) 的优化构建方式二、android httpClient(https/http)的优化构建方式一、Android HTTPPost返回错误“方法不允许”的实用信息。

本文目录一览:

Android 8:不允许明文 HTTP 流量(安卓11不允许访问data)

Android 8:不允许明文 HTTP 流量(安卓11不允许访问data)

我收到来自 Android 8 用户的报告,称我的应用(使用后端提要)不显示内容。经过调查,我发现 Android 8 上发生了以下异常:

08-29 12:03:11.246 11285-11285/ E/: [12:03:11.245,main]: Exception: IOException java.io.IOException: Cleartext HTTP traffic to * not permitted
at com.android.okhttp.HttpHandler$CleartextURLFilter.checkURLPermitted(HttpHandler.java:115)
at com.android.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:458)
at com.android.okhttp.internal.huc.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:127)
at com.deiw.android.generic.tasks.AbstractHttpAsyncTask.doConnection(AbstractHttpAsyncTask.java:207)
at com.deiw.android.generic.tasks.AbstractHttpAsyncTask.extendedDoInBackground(AbstractHttpAsyncTask.java:102)
at com.deiw.android.generic.tasks.AbstractAsyncTask.doInBackground(AbstractAsyncTask.java:88)
at android.os.AsyncTask$2.call(AsyncTask.java:333)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:245)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
at java.lang.Thread.run(Thread.java:764)

(我已经删除了包名、URL 和其他可能的标识符)

在 Android 7 及更低版本上一切正常,我没有android:usesCleartextTraffic在 Manifest 中进行设置(并且将其设置为true无济于事,无论如何这是默认值),我也没有使用网络安全信息。如果我调用NetworkSecurityPolicy.getInstance().isCleartextTrafficPermitted(),它会返回falseAndroid 8,true对于旧版本,使用相同的 apk 文件。我试图在有关 Android O 的 Google 信息中找到一些提及,但没有成功。

Android 8:不允许使用Cleartext HTTP流量

Android 8:不允许使用Cleartext HTTP流量

我收到了Android 8用户的报告,我的应用(使用后端Feed)没有显示内容.经过调查,我发现在Android 8上发生以下异常:

08-29 12:03:11.246 11285-11285/ E/: [12:03:11.245, main]: Exception: IOException java.io.IOException: Cleartext HTTP traffic to * not permitted
at com.android.okhttp.HttpHandler$CleartextURLFilter.checkURLPermitted(HttpHandler.java:115)
at com.android.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:458)
at com.android.okhttp.internal.huc.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:127)
at com.deiw.android.generic.tasks.AbstractHttpAsyncTask.doConnection(AbstractHttpAsyncTask.java:207)
at com.deiw.android.generic.tasks.AbstractHttpAsyncTask.extendedDoInBackground(AbstractHttpAsyncTask.java:102)
at com.deiw.android.generic.tasks.AbstractAsyncTask.doInBackground(AbstractAsyncTask.java:88)
at android.os.AsyncTask$2.call(AsyncTask.java:333)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:245)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
at java.lang.Thread.run(Thread.java:764)

(我删除了包名,URL和其他可能的标识符)

在Android 7及更低版本上一切正常,我没有在Manifest中设置android:usesCleartextTraffic(并将其设置为true无效,无论如何都是默认值),我也不使用网络安全信息.如果我调用NetworkSecurityPolicy.getInstance().isCleartextTrafficPermitted(),它将为Android 8返回false,对于旧版本,则返回true,使用相同的apk文件.
我试图在关于Android O的Google信息上找到一些提及,但没有成功.

解决方法:

根据Network security configuration –

Starting with Android 9 (API level 28), cleartext support is disabled
by default.

另外看看 – https://koz.io/android-m-and-the-war-on-cleartext-traffic/

选项1 –

创建文件res / xml / network_security_config.xml –

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config cleartextTrafficPermitted="true">
        <domain includeSubdomains="true">Your URL(ex: 127.0.0.1)</domain>
    </domain-config>
</network-security-config>

AndroidManifest.xml –

<?xml version="1.0" encoding="utf-8"?>
<manifest ...>
    <uses-permission android:name="android.permission.INTERNET" />
    <application
        ...
        android:networkSecurityConfig="@xml/network_security_config"
        ...>
        ...
    </application>
</manifest>

选项2 –

android:usesCleartextTraffic Doc

AndroidManifest.xml –

<?xml version="1.0" encoding="utf-8"?>
<manifest ...>
    <uses-permission android:name="android.permission.INTERNET" />
    <application
        ...
        android:usesCleartextTraffic="true"
        ...>
        ...
    </application>
</manifest>

另外正如@david.s’ answer指出android:targetSandBoxVersion也可能是一个问题 –

根据Manifest Docs –

android:targetSandBoxVersion

The target sandBox for this app to use. The higher the sandBox version
number, the higher the level of security. Its default value is 1; you
can also set it to 2. Setting this attribute to 2 switches the app to
a different SELinux sandBox. The following restrictions apply to a
level 2 sandBox:

  • The default value of usesCleartextTraffic in the Network Security Config is false.
  • Uid sharing is not permitted.

选项3 –

如果你在< manifest>中有android:targetSandBoxVersion然后将其减少到1

AndroidManifest.xml –

<?xml version="1.0" encoding="utf-8"?>
<manifest android:targetSandBoxVersion="1">
    <uses-permission android:name="android.permission.INTERNET" />
    ...
</manifest>

android httpClient (https/http) 的优化构建方式二

android httpClient (https/http) 的优化构建方式二

由于时间原因,这里只构建所有请求 https 都能通过的请求 client

首先定义(重写)信任管理器

    public class SSLTrustManager implements javax.net.ssl.TrustManager,
            javax.net.ssl.X509TrustManager ,HostnameVerifier{
        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
            return null;
        }
 
        public boolean isServerTrusted(
                java.security.cert.X509Certificate[] certs) {
            return true;
        }
 
        public boolean isClientTrusted(
                java.security.cert.X509Certificate[] certs) {
            return true;
        }
 
        public void checkServerTrusted(
                java.security.cert.X509Certificate[] certs, String authType)
                throws java.security.cert.CertificateException {
            return;
        }
 
        public void checkClientTrusted(
                java.security.cert.X509Certificate[] certs, String authType)
                throws java.security.cert.CertificateException {
            return;
        }
        
        	@Override
		public boolean verify(String urlHostName, SSLSession session) //允许所有主机
			return true;
		}
    }

然后封装

 public static HttpURLConnection connect(String strUrl) throws Exception {
        
    	 javax.net.ssl.TrustManager[] trustAllCerts = new javax.net.ssl.TrustManager[1];
         javax.net.ssl.TrustManager tm = new SSLTrustManager();
         trustAllCerts[0] = tm;
         javax.net.ssl.SSLContext sc = javax.net.ssl.SSLContext
                 .getInstance("SSL");
         sc.init(null, trustAllCerts, null);
         javax.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory(sc
                 .getSocketFactory());
         
         HttpsURLConnection.setDefaultHostnameVerifier((HostnameVerifier) tm);
         
        URL url = new URL(strUrl);
        HttpURLConnection urlConn = (HttpURLConnection) url.openConnection();
     
        return urlConn;
    }

测试一下

public void unitest()
{
     HttpURLConnection urlConn = connect(''https://github.com/'');
        urlConn.setDoInput(true);
        urlConn.setRequestMethod("GET");
        urlConn.connect();
        InputStream is = urlConn.getInputStream();
        StringBuffer sb = new StringBuffer();
        if(is!=null)
        {
        	BufferedReader br = new BufferedReader(new InputStreamReader(is));
        	String s = null;
        	while ((s=br.readLine())!=null)
        	{
				sb.append(s).append("\n");
			}
        }
        System.out.println("Content:\n"+sb.toString());
}

try do it!

android httpClient(https/http)的优化构建方式一

android httpClient(https/http)的优化构建方式一

参考:基于java的https双向认证,android上亦可用

Android Https相关完全解析 当OkHttp遇到Https


在android中,经常不可避免的的使用https和http访问网络数据,因此需要先构建一个网络访问client

其中https的访问需要使用SSLSocket,但对于一般安全性要求不是很高的通信,一般设置SSLSocket是允许所有主机通过,下面给出2中,一种是允许所有主机通过的https通信,另一种是加密传输,需要使用cert证书。

允许所有主机通过

public class GlobalUtils
{
    
     public static HttpClient getAndroidHttpClient(int connTimeout, String userAgent,int retryTimes)
     {
              AbstractHttpClient httpClient = null;
	      //设置请求控制参数
	      HttpParams params = new BasicHttpParams();
	      ConnManagerParams.setTimeout(params, connTimeout);
	      HttpConnectionParams.setSoTimeout(params, connTimeout);
	      HttpConnectionParams.setConnectionTimeout(params, connTimeout);

	        if (TextUtils.isEmpty(userAgent)) {
	            userAgent = System.getProperty("http.agents", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.124 Safari/537.36");
	        }
	        HttpProtocolParams.setUserAgent(params, userAgent);
               //设置最大的连接数
	        ConnManagerParams.setMaxConnectionsPerRoute(params, new ConnPerRouteBean(10));
	        ConnManagerParams.setMaxTotalConnections(params, 10);

	        HttpConnectionParams.setTcpNoDelay(params, true); //关闭Socket缓冲

	        HttpConnectionParams.setSocketBufferSize(params, 1024 * 8);//本方法与setTcpNoDelay冲突

	        HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);

	        SchemeRegistry schemeRegistry = new SchemeRegistry();
	        schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
	        schemeRegistry.register(new Scheme("https", DefaultSSLSocketFactory.getSocketFactory(), 443));

	        httpClient = new DefaultHttpClient(new ThreadSafeClientConnManager(params, schemeRegistry), params);
	        httpClient.setHttpRequestRetryHandler(new RetryHandler(retryTimes));

	        httpClient.addRequestInterceptor(new HttpRequestInterceptor() {
	            @Override
	            public void process(org.apache.http.HttpRequest httpRequest, HttpContext httpContext) throws org.apache.http.HttpException, IOException {
	                if (!httpRequest.containsHeader("Accept-Encoding")) {
	                    httpRequest.addHeader("Accept-Encoding", "gzip");
	                }
	            }
	        });

	        httpClient.addResponseInterceptor(new HttpResponseInterceptor() {
	            @Override
	            public void process(HttpResponse response, HttpContext httpContext) throws org.apache.http.HttpException, IOException {
	                final HttpEntity entity = response.getEntity();
	                if (entity == null) {
	                    return;
	                }
	                final Header encoding = entity.getContentEncoding();
	                if (encoding != null) {
	                    for (HeaderElement element : encoding.getElements()) {
	                        if (element.getName().equalsIgnoreCase("gzip")) {
	                            response.setEntity(new GZipDecompressingEntity(response.getEntity()));
	                            return;
	                        }
	                    }
	                }
	            }
	        });
			return httpClient;
	    }

}

上面的重试RetryHandler是请求失败后重试的规则

public class RetryHandler implements HttpRequestRetryHandler {  //需要实现HttpRequestRetryHandler

    private static final int RETRY_SLEEP_INTERVAL = 500;

    private static HashSet<Class<?>> exceptionWhiteList = new HashSet<Class<?>>();

    private static HashSet<Class<?>> exceptionBlackList = new HashSet<Class<?>>();

    static {
        exceptionWhiteList.add(NoHttpResponseException.class);
        exceptionWhiteList.add(UnknownHostException.class);
        exceptionWhiteList.add(SocketException.class);

        exceptionBlackList.add(InterruptedIOException.class);
        exceptionBlackList.add(SSLHandshakeException.class);
    }

    private final int maxRetries;

    public RetryHandler(int maxRetries) {
        this.maxRetries = maxRetries;
    }

    @Override
    public boolean retryRequest(IOException exception, int retriedTimes, HttpContext context) {
        boolean retry = true;

        if (exception == null || context == null) {
            return false;
        }

        Object isReqSent = context.getAttribute(ExecutionContext.HTTP_REQ_SENT);
        boolean sent = isReqSent == null ? false : (Boolean) isReqSent;

        if (retriedTimes > maxRetries) {
            retry = false;
        } else if (exceptionBlackList.contains(exception.getClass())) {
            retry = false;
        } else if (exceptionWhiteList.contains(exception.getClass())) {
            retry = true;
        } else if (!sent) {
            retry = true;
        }

        if (retry) {
            try {
                Object currRequest = context.getAttribute(ExecutionContext.HTTP_REQUEST);
                if (currRequest != null) {
                //这里只允许GET请求的重试,因为在一般访问中POST重试会造成重复提交问题,因此不宜使用
                    if (currRequest instanceof HttpRequestBase) {
                        HttpRequestBase requestBase = (HttpRequestBase) currRequest;
                        retry = "GET".equals(requestBase.getMethod());
                    } else if (currRequest instanceof RequestWrapper) {
                        RequestWrapper requestWrapper = (RequestWrapper) currRequest;
                        retry = "GET".equals(requestWrapper.getMethod());
                    }
                } else {
                    retry = false;
                    LogUtils.e("retry error, curr request is null");
                }
            } catch (Throwable e) {
                retry = false;
                LogUtils.e("retry error", e);
            }
        }

        if (retry) {
            SystemClock.sleep(RETRY_SLEEP_INTERVAL); // sleep a while and retry http request again.
        }

        return retry;
    }

}

需要重写SSLSocketFactory来支持所有主机通过

public class DefaultSSLSocketFactory extends SSLSocketFactory {
    //ssl上下文环境
    private SSLContext sslContext = SSLContext.getInstance("TLS");
    //证书保存对象
    private static KeyStore trustStore;

    static {
        try {
            trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
            //通常这里需要加载证书
            trustStore.load(null, null);
        } catch (Throwable e) {
           e.printStackTrace();
        }
    }

    private static DefaultSSLSocketFactory instance;

    public static DefaultSSLSocketFactory getSocketFactory() {
        if (instance == null) {
            try {
                instance = new DefaultSSLSocketFactory();
            } catch (Throwable e) {
                LogUtils.e(e.getMessage(), e);
            }
        }
        return instance;
    }

    private DefaultSSLSocketFactory()
            throws UnrecoverableKeyException,
            NoSuchAlgorithmException,
            KeyStoreException,
            KeyManagementException {
        super(trustStore);

        TrustManager trustAllCerts = new X509TrustManager() {
            public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                return null;
            }

            @Override
            public void checkClientTrusted(
                    java.security.cert.X509Certificate[] chain, String authType)
                    throws java.security.cert.CertificateException {
            }

            @Override
            public void checkServerTrusted(
                    java.security.cert.X509Certificate[] chain, String authType)
                    throws java.security.cert.CertificateException {
            }
        };
        //初始化509凭证信任管理器
        sslContext.init(null, new TrustManager[]{trustAllCerts}, null);
        //设置所有请求都会得到客户端的信任
        this.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
    }

    @Override
    public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException {
        //连接SSL Socket
        return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
    }

    @Override
    public Socket createSocket() throws IOException {
        return sslContext.getSocketFactory().createSocket();
    }
}

当然上面的通信谈不上SSL加密,因此使用了https和没使用https请求没啥区别,就像http一样。


对于https安全请求的的加密过程,我们需要充分的认识,简单的说他是一个加密的过程。

对于这个过程的请求才叫安全请求,那么这个请求是怎么构建的呢

一般来说证书放在assets或者raw资源文件下(以下代码来自互联网,用户可以再第一段代码中稍作修改,便可使用)

public void getHttpsKeyStore(){
    AssetManager am = context.getAssets(); 
    InputStream ins = am.open("robusoft.cer"); 
    try {
            //读取证书
            CertificateFactory cerFactory = CertificateFactory.getInstance("X.509");  //问1
            Certificate cer = cerFactory.generateCertificate(ins);
            //创建一个证书库,并将证书导入证书库
            KeyStore keyStore = KeyStore.getInstance("PKCS12", "BC");   //问2
            keyStore.load(null, null);
            keyStore.setCertificateEntry("trust", cer);
            return keyStore;
    } finally {
            ins.close();
    }
  
}

将这里代码整合到第一段代码中,形成https安全请求,当然也可以单独使用,

public static HttpClient getAndroidHttpClient(int connTimeout, String userAgent,int retryTimes)
{
//......
   AssetManager am = context.getAssets(); 
    InputStream ins = am.open("robusoft.cer"); 
    try {
            //读取证书
            CertificateFactory cerFactory = CertificateFactory.getInstance("X.509");  //问1
            Certificate cer = cerFactory.generateCertificate(ins);
            //创建一个证书库,并将证书导入证书库
            KeyStore keyStore = KeyStore.getInstance("PKCS12", "BC");   //问2
            keyStore.load(null, null);
            keyStore.setCertificateEntry("trust", cer);
             //把咱的证书库作为信任证书库
            SSLSocketFactory socketFactory = new SSLSocketFactory(keystore);
            schemeRegistry.register(new Scheme("https", socketFactory , 443));
    } finally {
            ins.close();
    }
  //  ......
  
  }


Android HTTPPost返回错误“方法不允许”

Android HTTPPost返回错误“方法不允许”

我正在编写一个Android 2.2应用程序,该应用程序将JSON严格性过帐到ReSTfull Web服务。

Fiddler对Web服务的调用具有与预期相同的Json返回,而对ASPX Web应用程序具有与预期的相同Json返回。

当我查看服务器日志时,可以看到服务器使用307重定向响应初始POST动词,然后立即响应GET和405错误。

Fiddler和aspx应用程序记录一个307重定向的POST,然后立即另一个POST和200 OK。

到底是怎么回事?

这是主要活动:

package com.altaver.android_PostJson2;

import org.json.JSONException;
import org.json.JSONObject;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;

public class PostJson extends Activity {
     private static final String TAG = "MainActivity";
     private static final String URL = "http://web2.altaver.com/sdz/avReSTfulLogin1";

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        JSONObject jsonObjSend = new JSONObject();

        try {
         jsonObjSend.put("Pass","sz");
         jsonObjSend.put("User","szechman");


         Log.i(TAG,jsonObjSend.toString(2));

        } catch (JSONException e) {
            e.printStackTrace();
        }

        JSONObject jsonObjRecv = HttpClient.SendHttpPost(URL,jsonObjSend);

//examine JSONObject later
    }
}

这是进行Web服务调用的类代码:

package com.altaver.android_PostJson2;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.params.HttpClientParams;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONObject;

import android.util.Log;

public class HttpClient {

    private static final String TAG = "HttpClient";


    public static JSONObject SendHttpPost(String URL,JSONObject jsonObjSend) {

          try {
           DefaultHttpClient httpclient = new DefaultHttpClient();

           HttpClientParams.setRedirecting(httpclient.getParams(),true);

           //added cookie policy,wild shot in the dark
           //httpclient.getParams().setParameter(ClientPNames.COOKIE_POLICY,>CookiePolicy.RFC_2109);

           HttpPost httpPostRequest = new HttpPost(URL);

           StringEntity se;
           se = new StringEntity(jsonObjSend.toString());

           // Set HTTP parameters
           httpPostRequest.setEntity(se);

           //httpPostRequest.setHeader("User-Agent",>"com.altaver.android_PostJson2");
           httpPostRequest.setHeader("User-Agent","Mozilla/5.0 (Windows; U; >Windows NT 5.1; en-US; rv:1.9.2.3) Gecko/20100401");

           httpPostRequest.setHeader("Accept","application/json");
           httpPostRequest.setHeader("Content-Type","application/json");

           long t = System.currentTimeMillis();
           HttpResponse response = (HttpResponse) >httpclient.execute(httpPostRequest);
           Log.i(TAG,"HTTPResponse received in [" + >(System.currentTimeMillis()-t) + "ms]");

           HttpEntity entity = response.getEntity();

           if (entity != null) {
            InputStream instream = entity.getContent();
            Header contentEncoding = response.getFirstHeader("Content-Encoding");


            String resultString= convertStreamToString(instream);
            instream.close();
            resultString = resultString.substring(1,resultString.length()-1); // >remove wrapping "[" and "]"

            JSONObject jsonObjRecv = new JSONObject(resultString);
            Log.i(TAG,"<jsonobject>\n"+jsonObjRecv.toString()+"\n</jsonobject>");

            return jsonObjRecv;
           }

          }
          catch (Exception e)
          {
           e.printStackTrace();
          }
          return null;
         }

    private static String convertStreamToString(InputStream is) {
          /*
           * To convert the InputStream to String we use the >BufferedReader.readLine()
           * method. We iterate until the BufferedReader return null which means
           * there's no more data to read. Each line will appended to a >StringBuilder
           * and returned as String.
           * 
           * (c) public domain: http://senior.ceng.metu.edu.tr/2009/praeda/2009/01>/11/a-simple-restful-client-at-android/
           */
          BufferedReader reader = new BufferedReader(new InputStreamReader(is));
          StringBuilder sb = new StringBuilder();

          String line = null;
          try {
           while ((line = reader.readLine()) != null) {
            sb.append(line + "\n");
           }
          } catch (IOException e) {
           e.printStackTrace();
          } finally {
           try {
            is.close();
           } catch (IOException e) {
            e.printStackTrace();
           }
          }
          return sb.toString();
    }
}

今天关于Android 8:不允许明文 HTTP 流量安卓11不允许访问data的介绍到此结束,谢谢您的阅读,有关Android 8:不允许使用Cleartext HTTP流量、android httpClient (https/http) 的优化构建方式二、android httpClient(https/http)的优化构建方式一、Android HTTPPost返回错误“方法不允许”等更多相关知识的信息可以在本站进行查询。

本文标签: