You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 09:01:17