Android中SOAP调用求助:WebService与Android开发新手咨询
嘿,刚接触WebService和Android开发的话,确实需要一步步来捋清楚。我帮你拆解一下怎么实现SOAP调用,还要确保和你提供的PHP AES解密逻辑完全匹配哦!
第一步:准备Android端的SOAP依赖
Android原生没有内置SOAP支持,我们常用ksoap2-android库来处理SOAP请求。在你的Module级别build.gradle文件里添加依赖:
implementation 'com.google.code.ksoap2-android:ksoap2-android:3.6.4'
添加完记得同步项目,让Gradle下载依赖包。
第二步:对齐服务端的AES解密逻辑
先看你提供的PHP代码,核心是AES-256-CBC模式,有几个关键规则必须在Android端严格对应:
- 如果密钥不是32位,会用SHA256哈希生成32位二进制密钥
- IV和密钥同源,如果不是16位,会用MD5哈希生成16位二进制IV
- 解密后要移除PKCS7填充(最后一个字节代表填充长度)
第三步:实现Android端的AES解密工具类
写一个和服务端逻辑完全一致的工具类,避免解密失败:
import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.security.MessageDigest; import java.util.Arrays; public class AESUtils { public static String aes256CbcDecrypt(String key, String encryptedData) { try { // 处理密钥:非32位则用SHA256哈希 byte[] keyBytes; if (key.getBytes().length != 32) { MessageDigest sha256 = MessageDigest.getInstance("SHA-256"); keyBytes = sha256.digest(key.getBytes()); } else { keyBytes = key.getBytes(); } // 处理IV:和密钥同源,非16位则用MD5哈希 byte[] ivBytes; if (key.getBytes().length != 16) { MessageDigest md5 = MessageDigest.getInstance("MD5"); ivBytes = md5.digest(key.getBytes()); } else { ivBytes = key.getBytes(); } // 解码加密数据(默认服务端返回Base64格式,若为十六进制需修改此处) byte[] encryptedBytes = android.util.Base64.decode(encryptedData, android.util.Base64.DEFAULT); // 初始化AES解密器 Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES"); IvParameterSpec ivSpec = new IvParameterSpec(ivBytes); cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivSpec); // 执行解密并移除填充 byte[] decryptedBytes = cipher.doFinal(encryptedBytes); int paddingLength = decryptedBytes[decryptedBytes.length - 1]; return new String(Arrays.copyOfRange(decryptedBytes, 0, decryptedBytes.length - paddingLength)); } catch (Exception e) { e.printStackTrace(); return ""; } } }
第四步:实现SOAP调用核心逻辑
网络请求不能在主线程执行,这里提供两种常见实现方式:
方式1:用AsyncTask(适合旧版Android Java项目)
import android.os.AsyncTask; import org.ksoap2.SoapEnvelope; import org.ksoap2.serialization.SoapObject; import org.ksoap2.serialization.SoapSerializationEnvelope; import org.ksoap2.transport.HttpTransportSE; public class SoapCallTask extends AsyncTask<Void, Void, String> { // 替换成你实际的WebService参数 private static final String NAMESPACE = "http://your-service-namespace.com/"; private static final String URL = "http://your-service-url.com/your-webservice.php"; private static final String METHOD_NAME = "aes256_cbc_decrypt"; private static final String SOAP_ACTION = NAMESPACE + METHOD_NAME; private String secretKey = "your-actual-secret-key"; private String encryptedDataToDecrypt = "your-encrypted-data-from-server"; @Override protected String doInBackground(Void... voids) { try { // 构建SOAP请求对象 SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME); request.addProperty("key", secretKey); // 参数名要和PHP方法的$key完全一致 request.addProperty("data", encryptedDataToDecrypt); // 参数名和PHP的$data完全一致 // 创建SOAP信封,版本对应服务端支持的SOAP版本(VER11对应SOAP 1.1) SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11); envelope.setOutputSoapObject(request); envelope.dotNet = false; // PHP服务端一般设置为false // 发送请求并获取响应 HttpTransportSE transport = new HttpTransportSE(URL); transport.call(SOAP_ACTION, envelope); SoapObject response = (SoapObject) envelope.getResponse(); String encryptedResult = response.getPropertyAsString(0); // 解密服务端返回的结果 return AESUtils.aes256CbcDecrypt(secretKey, encryptedResult); } catch (Exception e) { e.printStackTrace(); return "请求失败:" + e.getMessage(); } } @Override protected void onPostExecute(String result) { super.onPostExecute(result); // 在这里处理返回结果,比如显示到TextView或Toast // Toast.makeText(context, result, Toast.LENGTH_LONG).show(); } }
方式2:用Coroutine(适合AndroidX Kotlin项目)
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.ksoap2.SoapEnvelope import org.ksoap2.serialization.SoapObject import org.ksoap2.serialization.SoapSerializationEnvelope import org.ksoap2.transport.HttpTransportSE fun callSoapService(secretKey: String, encryptedData: String) { GlobalScope.launch(Dispatchers.Main) { val result = withContext(Dispatchers.IO) { try { val NAMESPACE = "http://your-service-namespace.com/" val URL = "http://your-service-url.com/your-webservice.php" val METHOD_NAME = "aes256_cbc_decrypt" val SOAP_ACTION = "$NAMESPACE$METHOD_NAME" val request = SoapObject(NAMESPACE, METHOD_NAME) request.addProperty("key", secretKey) request.addProperty("data", encryptedData) val envelope = SoapSerializationEnvelope(SoapEnvelope.VER11) envelope.setOutputSoapObject(request) envelope.dotNet = false val transport = HttpTransportSE(URL) transport.call(SOAP_ACTION, envelope) val response = envelope.response as SoapObject val encryptedResult = response.getPropertyAsString(0) // 解密结果 AESUtils.aes256CbcDecrypt(secretKey, encryptedResult) } catch (e: Exception) { "请求失败:${e.message}" } } // 更新UI,比如赋值给TextView // binding.resultTv.text = result } } // Kotlin版AES工具类 object AESUtils { fun aes256CbcDecrypt(key: String, encryptedData: String): String { return try { val keyBytes = if (key.toByteArray().size != 32) { MessageDigest.getInstance("SHA-256").digest(key.toByteArray()) } else { key.toByteArray() } val ivBytes = if (key.toByteArray().size != 16) { MessageDigest.getInstance("MD5").digest(key.toByteArray()) } else { key.toByteArray() } val encryptedBytes = android.util.Base64.decode(encryptedData, android.util.Base64.DEFAULT) val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding") val secretKeySpec = SecretKeySpec(keyBytes, "AES") val ivSpec = IvParameterSpec(ivBytes) cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivSpec) val decryptedBytes = cipher.doFinal(encryptedBytes) val paddingLength = decryptedBytes.last().toInt() String(decryptedBytes.copyOfRange(0, decryptedBytes.size - paddingLength)) } catch (e: Exception) { e.printStackTrace() "" } } }
第五步:必加的配置和注意事项
- 网络权限:在
AndroidManifest.xml中添加网络权限<uses-permission android:name="android.permission.INTERNET" /> - 参数匹配:SOAP请求的参数名必须和PHP方法的参数名(
$key、$data)完全一致,否则服务端无法接收参数 - 命名空间和URL:一定要替换成你实际的WebService命名空间和地址,PHP服务端的命名空间通常是服务URL或自定义的字符串
- 加密格式:如果服务端返回的是十六进制字符串而非Base64,需要修改工具类中的解码逻辑,换成十六进制转字节数组的方法
内容的提问来源于stack exchange,提问作者Niño Cuenco
相关产品推荐
相关产品推荐

