Android Jellybean 4.1.2下用SpongyCastle实现TLS1.2登录遇故障求助
Hey there, let's break down and fix the two issues you're hitting when trying to connect to your TLS 1.2 server with SpongyCastle on that old Jellybean device.
问题1:收到HTTP 302重定向而非JSON响应
First off, that 302 usually means the server isn't getting what it expects from your client, so it's sending you elsewhere instead of the JSON login response. Here's how to debug and fix this:
确保SpongyCastle正确覆盖系统SSL上下文
Android 4.1默认不支持TLS 1.2,所以你需要强制连接使用SpongyCastle的实现。先初始化Provider并创建支持TLS 1.2的SSLContext:// 先初始化SpongyCastle Security.insertProviderAt(new org.spongycastle.jce.provider.BouncyCastleProvider(), 1); // 创建TLS 1.2的SSLContext SSLContext sslContext = SSLContext.getInstance("TLSv1.2", "SC"); sslContext.init(null, null, new SecureRandom());后续一定要把这个SSLContext的socket工厂绑定到你的
HttpURLConnection上。添加正确的请求头
服务器通常会检查Accept和User-Agent这类头来决定返回什么内容。如果不指定Accept: application/json,服务器可能默认返回HTML页面(这就是重定向的原因)。给连接加上这些头:conn.setRequestProperty("Accept", "application/json"); conn.setRequestProperty("User-Agent", "YourAppName/1.0 (Android 4.1.2)");禁用自动重定向以排查问题
默认HttpURLConnection会自动跟随302跳转,这会掩盖背后的问题。关掉自动跳转,就能看到它要重定向到哪里、为什么:conn.setInstanceFollowRedirects(false);请求发送后,检查响应里的
Location头。如果指向HTTP页面(不是HTTPS),可能你初始请求用了HTTP而非HTTPS;如果是意料外的登录页面,要检查请求URL和凭证是否正确。验证证书处理逻辑
如果SpongyCastle不信任服务器证书,握手可能静默失败,服务器就会把你重定向到错误页面。测试阶段可以加一个接受所有证书的自定义TrustManager(生产环境绝对不能这么做!):TrustManager[] trustAllCerts = new TrustManager[]{ new X509TrustManager() { public X509Certificate[] getAcceptedIssuers() { return null; } public void checkClientTrusted(X509Certificate[] certs, String authType) {} public void checkServerTrusted(X509Certificate[] certs, String authType) {} } }; sslContext.init(null, trustAllCerts, new SecureRandom());
问题2:如何为其他接口发送JSON请求体
在Android 4.1上用HttpURLConnection发送JSON很简单,只要设置好正确的头和输出流就行。这里是分步示例:
配置连接为POST并允许输出
URL url = new URL("https://your-server.com/api/your-endpoint"); HttpsURLConnection conn = (HttpsURLConnection) url.openConnection(); // 绑定之前创建的SpongyCastle TLS 1.2 socket工厂 conn.setSSLSocketFactory(sslContext.getSocketFactory()); conn.setRequestMethod("POST"); conn.setDoOutput(true); // 允许写入请求体设置Content-Type头
告诉服务器你要发送的是JSON:conn.setRequestProperty("Content-Type", "application/json; charset=utf-8");写入JSON请求体
把登录凭证(或其他数据)转成JSON字符串,写入连接的输出流:String jsonPayload = "{\"username\":\"your-username\",\"password\":\"your-password\"}"; OutputStream os = conn.getOutputStream(); os.write(jsonPayload.getBytes("UTF-8")); os.flush(); os.close();读取响应
发送请求后,读取服务器响应来确认是否拿到了预期的JSON:BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream())); String line; StringBuilder response = new StringBuilder(); while ((line = br.readLine()) != null) { response.append(line); } br.close(); // 在这里解析JSON响应
最后要注意:所有网络操作都要放在后台线程(不能在主线程),否则会触发NetworkOnMainThreadException——这是Android 4.x的常见坑哦。
内容的提问来源于stack exchange,提问作者eoinzy

