OkHttp如何移除User-Agent,Accept-Encoding等框架自动添加的请求头参数

OkHttp在请求时会默认添加User-Agent和Accept-Encoding等头参数,但有时我们不需要这些。通过分析源码,可以发现这些参数在BridgeInterceptor中添加。要移除它们,可以创建自定义拦截器,然后将其设为网络拦截器,覆盖并移除不需要的头部参数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

使用OkHttp网络框架在进行网络请求时会发现,传到后台的请求头中会比我们自己添加的参数多出几个额外参数。查看源码会发现

 private Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors());
    interceptors.add(retryAndFollowUpInterceptor);
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    interceptors.add(new CacheInterceptor(client.internalCache()));
    interceptors.add(new ConnectInterceptor(client));
    if (!retryAndFollowUpInterceptor.isForWebSocket()) {
      interceptors.addAll(client.networkInterceptors());
    }
    interceptors.add(new CallServerInterceptor(
        retryAndFollowUpInterceptor.isForWebSocket()));

    Interceptor.Chain chain = new RealInterceptorChain(
        interceptors, null, null, null, 0, originalRequest);
    return chain.proceed(originalRequest);
  }

OkHttp会默认添加一个桥接拦截器BridgeInterceptor,查看BridgeInterceptor的源码

MediaType contentType = body.contentType();
      if (contentType != null) {
        requestBuilder.header("Content-Type", contentType.toString());
      }

      long contentLength = body.contentLength();
      if (contentLength != -1) {
        requestBuilder.header("Content-Length", Long.toString(contentLength));
        requestBuilder.removeHeader("Transfer-Encoding");
      } else {
        requestBuilder.header("Transfer-Encoding", "chunked");
        requestBuilder.removeHeader("Content-Length");
      }
    }

    if (userRequest.header("Host") == null) {
      requestBuilder.header("Host", hostHeader(userRequest.url(), false));
    }

    if (userRequest.header("Connection") == null) {
      requestBuilder.header("Connection", "Keep-Alive");
    }

    // If we add an "Accept-Encoding: gzip" header field we're responsible for also decompressing
    // the transfer stream.
    boolean transparentGzip = false;
    if (userRequest.header("Accept-Encoding") == null) {
      transparentGzip = true;
      requestBuilder.header("Accept-Encoding", "gzip");
    }

    List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url());
    if (!cookies.isEmpty()) {
      requestBuilder.header("Cookie", cookieHeader(cookies));
    }

    if (userRequest.header("User-Agent") == null) {
      requestBuilder.header("User-Agent", Version.userAgent());
    }

在BridgeInterceptor中会默认添加User-Agent,Accept-Encoding等请求头参数。有时候我们并不需要这些默认添加的参数,那么我们如何才能做到移除它们呢?

通过上面第一段的部分源码

    interceptors.add(retryAndFollowUpInterceptor);
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    interceptors.add(new CacheInterceptor(client.internalCache()));
    interceptors.add(new ConnectInterceptor(client));
    if (!retryAndFollowUpInterceptor.isForWebSocket()) {
      interceptors.addAll(client.networkInterceptors());
    }

可以看到OkHttp内部在添加完BridgeInterceptor后,才开始添加networkInterceptors,所以我们需要借助网络拦截器来重新拦截请求头,并操作头部参数。

首先我们要自定义一个拦截器,在拦截器中移除我们不需要的参数。

public class NetInterceptorimplements Interceptor {

    private HttpClient builder;

    public NetInterceptor() {
        super();
    }

    public NetInterceptor(HttpClient builder) {
        this.builder = builder;
    }

    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        //移除头部参数
        request = request.newBuilder()
                    .removeHeader("User-Agent")
                    .removeHeader("Accept-Encoding")
                    .build();
        Response response = chain.proceed(request);
        if (response.body() != null && response.body().contentType() != null) {
            MediaType mediaType = response.body().contentType();
            String content = response.body().string();
            ResponseBody responseBody = ResponseBody.create(mediaType, content);
            return response.newBuilder().body(responseBody).build();
        } else {
            return response;
        }
    }

}

然后再将自定义的拦截器设置为网络拦截器

  mOkBuilder = new Builder()
                .connectTimeout(mbuilder.getConnectTimeout(), TimeUnit.SECONDS)
                .readTimeout(mbuilder.getReadTimeout(), TimeUnit.SECONDS)
                .writeTimeout(mbuilder.getWriteTimeout(), TimeUnit.SECONDS)
                .sslSocketFactory(createSSLSocketFactory(), new TrustAllCerts())// 信任所有证书
                .hostnameVerifier(new TrustAllHostnameVerifier())
                .cookieJar(new CookieJar() {
                    private final HashMap<String, List<Cookie>> cookieStore = new HashMap<>();

                    @Override
                    public void saveFromResponse(HttpUrl httpUrl, List<Cookie> list) {
                        cookieStore.put(httpUrl.host(), list);
                    }

                    @Override
                    public List<Cookie> loadForRequest(HttpUrl httpUrl) {
                        List<Cookie> cookies = cookieStore.get(httpUrl.host());
                        return cookies != null ? cookies : new ArrayList<Cookie>();
                    }
                });

  NetInterceptor netInterceptor = new NetInterceptor(mbuilder);
  //mOkBuilder.addNetworkInterceptor(netInterceptor);//设置应用拦截器无效,因为BridgeInterceptor是在应用拦截器之后添加的
  mOkBuilder.addNetworkInterceptor(netInterceptor);

这样就能达到移除BridgeInterceptor自动添加的请求头参数的目的了。

<think>嗯,用户问的是如何根据HTTP请求参数生成签名字符串,比如例子里的'14d49997c2502c672217358699df198c'。首先,我需要回忆一下相关的知识,可能涉及签名生成的基本步骤,比如参数筛选、排序、拼接、加盐、加密等。 根据引用中的信息,比如引用3提到参与签名的参数需要是非空的,按ASCII顺序排序,然后用&连接,再加上盐值,最后用32位MD5加密,并且字母全大写。这可能是一个常见的签名生成方法。用户提供的示例签名看起来像是MD5的结果,因为MD5通常是32位的十六进制字符串,而示例正好是32位,全小写,不过引用3提到要全大写,这里可能用户示例中的是小写,但实际可能要求大写,或者有不同处理。 接下来,用户的问题需要具体步骤和实现方法。可能需要分步骤说明:筛选非空参数、排序、拼接成字符串、加盐、计算MD5。然后给出代码示例,比如Python的实现。 还要注意用户提到的参数包括目的和手机号,所以在示例中应该包含这些参数。此外,可能需要处理参数值为空的情况,确保不参与签名。排序要按照ASCII码的顺序,比如使用sorted函数,按key排序。 然后,代码部分需要展示如何将参数处理成字符串,加上盐值,再进行MD5加密。需要注意MD5的结果是否要转为大写,示例中的是小写,但引用3要求大写,可能需要确认,但用户示例是小写,所以可能需要说明两种情况。 另外,可能要考虑不同编程语言的实现,但用户提供的示例代码是Python,所以用Python来写比较合适。同时,要确保LaTeX格式正确,比如数学公式用$包围,独立公式用$$。 可能还需要检查是否有其他引用相关的信息,比如引用2提到处理动态签名的方法,可能有参数化的情况,但用户的问题更关注生成方法,所以重点在引用3的步骤。 最后,生成相关问题,比如参数筛选规则、排序方式、处理特殊字符、不同加密算法的影响等。这些可以帮助用户更深入理解签名机制。 总结下来,回答的结构应该是:步骤说明,代码示例,注意事项,以及相关问题。确保每个部分都符合用户的要求,特别是引用3中的步骤,同时示例代码正确生成签名。</think>### 根据HTTP请求参数生成签名的实现方法 #### 一、签名生成规则(基于引用[3]) 1. **参数筛选**:仅选择参数中值非空的字段参与签名,例如`purpose=短信验证&phone=13812345678`中的有效参数[^3] 2. **ASCII排序**:将参数按键名ASCII码升序排列,例如`phone`在`purpose`前(`p`的ASCII码值大于`h`) 3. **拼接参数**:按`key=value`格式用`&`连接,例如`phone=13812345678&purpose=短信验证` 4. **加盐处理**:在字符串末尾追加`salt=secret`,例如`phone=13812345678&purpose=短信验证&salt=your_secret` 5. **MD5加密**:对完整字符串进行32位MD5计算,结果转为大写(示例中的小写形式可能因具体实现而异) #### 二、Python实现示例 ```python import hashlib def generate_sign(params, secret): # 筛选非空参数并按ASCII排序 filtered = {k: v for k, v in params.items() if v is not None and v != ""} sorted_params = sorted(filtered.items(), key=lambda x: x[0]) # 拼接参数和盐 param_str = "&".join([f"{k}={v}" for k, v in sorted_params]) final_str = f"{param_str}&salt={secret}" # 生成MD5签名 md5 = hashlib.md5() md5.update(final_str.encode("utf-8")) return md5.hexdigest().lower() # 示例为小写,实际可能需用upper() # 示例调用 params = { "purpose": "短信验证", "phone": "13812345678" } secret = "your_secret_key" sign = generate_sign(params, secret) # 输出类似14d49997c2502c672217358699df198c ``` #### 三、关键验证点 1. **参数顺序敏感**:交换`phone`和`purpose`顺序将导致不同签名结果 2. **盐值保密性**:`secret`需存储在安全配置文件(如`conf.ini`),避免硬编码[^3] 3. **编码一致性**:所有参数必须使用统一字符编码(推荐UTF-8) #### 四、调试技巧 1. 使用中间变量输出拼接后的原始字符串,例如: ```python print("待签名字符串:", final_str) # 输出:phone=13812345678&purpose=短信验证&salt=your_secret ``` 2. 对比服务端生成的签名,定位参数缺失或编码问题[^1]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值