如何使用Retrofit2的Interceptor实现请求体(Body)参数加密
嘿,我帮你排查下这个加密拦截器的问题,大概率是几个细节没处理到位导致加密后数据不正确,咱们一步步来理清楚:
可能的问题点及修复建议
加密结果的编码错误
如果你的encrypt()方法是直接把加密后的二进制字节转成UTF-8字符串,那肯定会出问题——加密后的二进制数据包含很多不可打印的控制字符,直接转UTF-8会丢失或篡改数据。正确的做法是把加密字节用Base64编码成可打印的字符串,这样才能安全地作为文本请求体传输。请求头未正确同步
你注释掉了Content-Type和Content-Length的设置,虽然OkHttp在部分场景下会自动处理,但保险起见必须显式更新这些头信息,尤其是当加密后的请求体类型、长度和原请求不一致时,后端很可能因为头信息不匹配无法正确解析请求体。空请求体的边界处理
代码里没有判断oldBody是否为空,遇到GET这类没有请求体的请求时会直接抛出空指针异常,得先做非空判断。
修改后的完整代码示例
import android.util.Base64; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import okhttp3.Buffer; import okhttp3.Interceptor; import okhttp3.MediaType; import okhttp3.Request; import okhttp3.RequestBody; import okhttp3.Response; public class EncryptionInterceptor implements Interceptor { private static final String TAG = EncryptionInterceptor.class.getSimpleName(); private static final boolean DEBUG = true; @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); RequestBody oldBody = request.body(); // 处理无请求体的请求(如GET) if (oldBody == null) { return chain.proceed(request); } // 读取原请求体内容,通过Buffer保证可重复读取 Buffer buffer = new Buffer(); oldBody.writeTo(buffer); String strOldBody = buffer.readUtf8(); buffer.reset(); // 重置buffer,避免影响后续拦截器读取 // 执行加密:这里假设encrypt接收字节数组,返回加密后的字节数组 byte[] plainBytes = strOldBody.getBytes(StandardCharsets.UTF_8); byte[] encryptedBytes = encrypt(plainBytes); // 将加密字节转成Base64字符串,确保传输安全 String strNewBody = Base64.encodeToString(encryptedBytes, Base64.DEFAULT); // 优先沿用原请求的MediaType,避免强制修改导致后端不兼容 MediaType mediaType = oldBody.contentType() != null ? oldBody.contentType() : MediaType.parse("application/json; charset=utf-8"); RequestBody newBody = RequestBody.create(mediaType, strNewBody); // 重新构建请求,显式更新请求头 Request newRequest = request.newBuilder() .method(request.method(), newBody) .header("Content-Type", mediaType.toString()) .header("Content-Length", String.valueOf(newBody.contentLength())) .build(); return chain.proceed(newRequest); } // 替换成你实际的加密逻辑(示例用SHA-256模拟,实际换成AES/RSA等) private byte[] encrypt(byte[] plainBytes) { try { MessageDigest digest = MessageDigest.getInstance("SHA-256"); return digest.digest(plainBytes); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); return plainBytes; } } }
额外注意事项
- 和后端确认好加密算法、密钥、编码方式(比如统一用UTF-8),以及请求体是否需要Base64编码,双方逻辑必须完全一致才能正确解密。
- 调试时可以打印
strOldBody和strNewBody,对比加密前后的内容是否符合预期,也可以抓包查看实际发送的请求体是否正确。 - 如果你的
encrypt()方法已经返回Base64编码后的字符串,直接使用即可,跳过字节转Base64的步骤。
内容的提问来源于stack exchange,提问作者Vishal Patel
相关产品推荐
相关产品推荐

