Java调用UniHttp接口请求失败?一次开源的深入实践-百度SN签名认证场景下参数乱序问题的三种解决策略

Java调用UniHttp接口请求失败?一次开源的深入实践-百度SN签名认证场景下参数乱序问题的三种解决策略

目录

前言

一、场景重现

1、UniHttp模式下SN接口定义

2、第一次正式调用

3、第一次遇到211 APP SN校验失败

二、问题查找及开源寻求解决方案

1、抽丝剥茧找到问题所在

2、与作者在Issues的交流

3、开源项目交流有感

三、朝着正确的方向解决问题

1、加密顺序按实际请求参数求解

2、一种兼容Get请求参数动态调整的方法

3、使用注解来设置重排序规则

4、重写请求的QueryParam重排序方法

5、三种方法的使用场景及对比

四、总结


前言

        在当今数字化时代,不管是内部系统之间还是跟外部系统的对接,接口调用已成为软件开发中不可或缺的一部分。Java作为一种广泛使用的编程语言,在接口调用方面有着丰富的应用。在现代软件架构中,分布式系统和微服务架构的广泛应用使得不同模块之间的通信主要依赖于接口调用。在之前的博文中,我们讲解了许多跟第三方接口对接的详细案例,比如如何调用实时航班数据、接入百度地图、高德地图、天地图、实时景区等多源数据,也讲解了一些数据接入方式,比如使用抓取器,也详细叙述过UniHttp框架来进行接口调用。如果是一些对请求参数顺序没有限制的接口中,一般来讲是可以正常进行集成的,而在一些需要二次验签的接口,比如一些在线地图的开放平台中就可以支持二次验签,以百度地图为例,需要二次验签的接口接入流程如下图所示:

        通过上图可以直观的看到,二次验签可以很好的对请求进行保护,用户可以随时修改二次验签的密钥,这样即使是不小心泄露了AK,也可以很好的避免AK的滥用,客户端需要使用sk进行参数验签,然后生成验签参数,服务端会使用同样的sk来进行同样的参数验证,如果匹配则放行,否则会拒绝访问,返回验证失败。在实际开发过程中,开发者们常常会遇到各种问题,其中请求参数乱序导致接口调用失败便是较为常见的一种。百度SN签名认证是一种常见的接口认证方式,它通过在请求中添加特定的签名参数来验证请求的合法性。在进行接口调用时,请求参数的顺序对于签名的生成至关重要。如果参数顺序发生混乱,生成的签名将与服务器端的预期不一致,从而导致接口请求被拒绝。请求参数乱序问题虽然看似微不足道,但实际上可能会给开发工作带来巨大的困扰。首先,它会导致接口调用失败,使得开发进度受阻。开发者需要花费大量时间排查问题,寻找导致乱序的原因。其次,这个问题可能会引发安全风险。如果签名认证机制被破坏,可能会导致未经授权的访问,从而威胁到系统的安全性和稳定性。因此,解决请求参数乱序问题对于确保接口调用的成功和系统的安全性至关重要。

        本文将以百度SN签名认证为例,深入探讨在Java调用UniHttp接口时,如何解决请求参数乱序问题,以确保接口请求的成功。在解决问题的同时,也跟开源作者进行了咨询,得到了作者的指导,对于问题的解决帮助非常大,因此在这里也简单讲讲与开源作者的交流过程及启发。

一、场景重现

        本节将详细介绍使用UniHttp模式下,接入第三方接口是,遇到SN的验签方式时会遇到验证失败的问题。通过重现接口调用现场,让大家对发生问题的背景有一个基本的认识,也为后面解决问题留下思考的空间。本节将从以下三个方面去讲解,首先我们按照普通的模式来进行UniHttp接口的接口定义,然后详细介绍SN模式的接口正式调用,最后详细说明可能遇到的问题。如果大家在使用UniHttp时没有遇到认证问题,那真的恭喜你,可以划走了。如果您也身处迷茫之中,不妨来这里看看。

1、UniHttp模式下SN接口定义

        我们首先按照官方文档的文档说明,在UniHttp中进行百度搜索接口的调用。按照UniHttp的处理请求规范,定义第一版的请求接口,接口定义如下:

package com.yelang.project.thridinterface; import com.burukeyou.uniapi.http.annotation.HttpApi; import com.burukeyou.uniapi.http.annotation.param.QueryPar; import com.burukeyou.uniapi.http.annotation.request.GetHttpInterface; import com.burukeyou.uniapi.http.core.response.HttpResponse; @HttpApi public interface BaiduGeoSearchWithSnService { /** * - 百度行政区划区域检索接口 * @param query 检索关键字。行政区划区域检索不支持多关键字检索。如果需要按POI分类进行检索,请将分类通过query参数进行设置,如query=美食 * @param region 检索行政区划区域(增加区域内数据召回权重,如需严格限制召回数据在区域内,请搭配使用city_limit参数),可输入行政区划名或对应 * @param output 输出格式为json或者xml * @param scope 检索结果详细程度。取值为1 或空,则返回基本信息;取值为2,返回检索POI详细信息 * @param ret_coordtype 返回的坐标类型,可选参数,添加后POI返回国测局经纬度坐标 * @param pageSize 单次召回POI数量,默认为10条记录,最大返回20条 * @param pageNum 分页页码,默认为0,0代表第一页 * @param ak 开发者的访问密钥,必填项 * @param sn 开发者的权限签名 * @return */ @GetHttpInterface(url="https://api.map.baidu.com/place/v2/search") public HttpResponse<String> getSearch(@QueryPar("query") String query, @QueryPar("region") String region, @QueryPar("output") String output, @QueryPar("scope") String scope, @QueryPar("ret_coordtype") String ret_coordtype, @QueryPar("page_size") int pageSize, @QueryPar("page_num") int pageNum, @QueryPar("ak") String ak, @QueryPar("sn") String sn); }

        与普通的接口请求一样,这里按照官方文档要求,这里只增加一个sn的签名参数。在调用的时候动态传入sn参数实现接口的调用。

2、第一次正式调用

        接下来还是常规操作,我们使用Junit来进行接口的测试,使用UniHttp来代理服务访问之后,接口的调用就比较简单了。直接在测试类中注入service实例后就可以进行服务的调用,关键代码如下:

package com.yelang.project.unihttp; import java.io.UnsupportedEncodingException; import java.util.LinkedHashMap; import java.util.Map; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import com.burukeyou.uniapi.http.core.response.HttpResponse; import com.yelang.project.thridinterface.BaiduGeoSearchSnProcessorService; import com.yelang.project.thridinterface.BaiduGeoSearchWithSnService; import com.yelang.project.thridinterface.signature.BaiduSignature; /** * - 百度检索2.0接口带SN访问服务示例 * @author 夜郎king * */ @SpringBootTest @RunWith(SpringRunner.class) public class BaiduGeoSearchWithSnServiceCase { /** * - 应用申请的AK值 */ private static final String AK_VALUE = "yourak"; /** * - 应用申请的SK值 */ private static final String SK_VALUE = "yoursk"; @Autowired private BaiduGeoSearchWithSnService bdGeoSearchWithSnService; @Test public void searchBySn() throws UnsupportedEncodingException { /** * -计算sn跟参数对出现顺序有关,get请求请使用LinkedHashMap保存<key,value>,该方法根据key的插入顺序排序; * -post请使用TreeMap保存<key,value>,该方法会自动将key按照字母a-z顺序排序。 * -所以get请求可自定义参数顺序(sn参数必须在最后)发送请求,但是post请求必须按照字母a-z顺序填充body(sn参数必须在最后)。 * -以get请求为例:https://api.map.baidu.com/geocoder/v2/?address=百度大厦&output=json&ak=yourak,paramsMap中先放入address, * -再放output,然后放ak,放入顺序必须跟get请求中对应参数的出现顺序保持一致。 */ Map<String, String> paramsMap = new LinkedHashMap<String, String>(); String api_prefix = "/place/v2/search?"; //按接口定义顺序来创建参数 String query = "湘菜"; String region = "158";// 158表示长沙市 String output = "json"; String scope = "2"; String ret_coordtype = "WGS84"; int pageSize = 20; int pageNum = 0; paramsMap.put("query", query); paramsMap.put("region", region); paramsMap.put("output", output); paramsMap.put("scope", scope); paramsMap.put("ret_coordtype", ret_coordtype); paramsMap.put("page_size", String.valueOf(pageSize)); paramsMap.put("page_num", String.valueOf(pageNum)); paramsMap.put("ak", AK_VALUE); // 调用签名工具生成SN BaiduSignature signature = new BaiduSignature(AK_VALUE, SK_VALUE,paramsMap,api_prefix); String sn = signature.getSnByMap(); System.out.println("sn==>" + sn); HttpResponse<String> result = bdGeoSearchWithSnService.getSearch(query, region, output, scope, ret_coordtype, pageSize, pageNum ,AK_VALUE,sn); System.out.println("第一页的数据如下:"); System.out.println(result); System.out.println(result.getBodyResult()); System.out.println("--------------------------------------------------------------"); } }

        在这里请注意,这里我们进行接口定义时,按照上层接口的定义时参数的有序性,我们在设置HashMap时使用的是LinkedHashMap来进行保存,让生成SN值的时候按照顺序来生成。万事俱备只欠东风,下面我们就可以直接运行调用函数,来测试一下接口是否可以联通。

3、第一次遇到211 APP SN校验失败

        通常来说,不出意外的话一定是出了意外。我们本来是充满了希望,认为直接将参数传入到UniHttp中进行携带访问即可,先来看下运行之后,在控制台中看到以下结果,如下图:

        首轮宣告失败,这是我们第一次遇到:{"status":211,"message":"APP SN校验失败"}。当然这个错误我们在之前的博客中曾经简单介绍过,在官方文档中也有所提及。如下图:

211APP SN校验失败SERVER类型APP有两种校验方式IP校验和SN校验,当用户请求的SN和服务端计算出来的SN不相等的时候提示SN校验失败

        出现SN校验失败的原因就是服务端的SN值和客户端计算的值不一样,导致不一样的可能有很多种,比如参数值顺序,还有就是ak和sk的值有变化,这些都是有可能的。那么使用UniHttp请求框架后,为什么会遇到这种问题呢?下面将针对这个问题来进行问题定位。

二、问题查找及开源寻求解决方案

        程序开发的过程就是解决问题的过程,遇到请求不通的情况确实比较容易让人焦急,但是如果能解决问题,那么自己又能积累更多的实战经验。遇到问题,我们根据官网的提示来大胆的猜测问题,即SN的计算生成问题,究竟是哪里出了问题?上面也分析了一些原因,比如参数的顺序等。既然是开源项目,我们就可以通过源码的定位来分析问题,也可以通过debug调试代码来进行辅助。

        本节分享博主如何抽丝剥茧的进行问题分析,最终找到了问题的所在。接着带着问题,通过GitHub上找到了开源作者,并且做了一些交流,通过信息的分享,进一步明确了问题的所在,从而解决了问题。这是一个特别有趣的过程,在与开源作者的沟通过程中,也能看到开源作者身上的韧劲和开源精神。

1、抽丝剥茧找到问题所在

        既然是参数生成的问题,那么我们就首先使用Debug的方式来看看到底是发生了什么?根据之前的经验,我们怀疑是请求参数的顺序的问题。因此我们可以在Eclipse中使用Debug的方式来看一下请求的参数顺序到底有没有问题,请求参数我们可以在HttpResponse对象中的值中进行获取,UniHttpResponse中可以看到:

        在HttpUrl对象中可以看到请求的地址和请求的参数,进一步打开这个HttpUrl对象的值:

HttpUrl( url=https://api.map.baidu.com/place/v2/search?, path=, anchor=null, queryParam={output=json, query=湘菜, scope=2, page_num=0, ak=yourak, s n=d1abf338a49bd82fca6992f5d2b9f686, region=158, ret_coordtype=WGS84, page_size=20}, pathParam={} )

        请注意,上面参数中queryParam与我们在接口中定义的顺序似乎不太一样,为了方便对比,我们整理成以下图片表格:

        可以看到在请求发送过程中,经过UniHttp的处理,参数的顺序已经发生了变化,因此可以得出结论,在请求过程中参数的顺序发生了变化,由此发生的请求,服务端拿到请求参数后,根据这个参数去加密计算得到的SN,与我们自己计算的一定是不一样的,所以就会发生SN验证失败的问题。现在基本是可以定位是这个问题。带着疑问我们就这个开源项目跟作者试着聊一聊。

2、与作者在Issues的交流

        带着问题,打开开源项目的开源地址,UniHttp开源地址。首先我们来看一下有关这个项目的Issues,或许在我之前已经有朋友遇到类似的问题,来看下列表界面:

        可以看到暂时还没有其它的博主有类似的问题,第一条其实博主本人与开源作者的实际交流情况。点开交流详情,是我们的详细交流过程:

        博主在Issue中描述了个人的使用问题和疑问,并咨询了作者是否可以设置顺序问题。也是非常快速的得到了作者的回复:

这个可以参考文档 https://burukeyou.github.io/UniHttp/#/pages/guide/core?id=_3%e3%80%81httpapiprocessor, 实现HttpApiProcessor并重写postBeforeHttpRequest方法,在里面调整参数顺序或者加签

        虽然在官方文档中看到,也亲自实践过这个方法,但是还是得为作者的迅速点赞。因为真的没有多久就得到了作者本文的回复。

3、开源项目交流有感

        UniHttp这个项目非常好用,在服务接口接入中提供了非常良好的便利性。也因此,使用的朋友非常多。在博主遇到相关的请求参数乱序的情况下,及时找到了开源地址,并且给予了留言说明了详细情况。中所周知,开源项目一般都是自己的副业或者第二职业,并不是随时随地的要看开源社区的消息和问题。但是这次的问题反馈和交流,却是异常的迅速,同时开源作者也指出了解决方法和实现路径。这是个人遇到的答复非常迅速及时的开源项目了,上一个如此迅速的项目是kkfileview。开源不易,希望大家都能找到解决办法,也能贡献更多的好项目,以后会更加深入的加入开源项目,为开源做贡献。

三、朝着正确的方向解决问题

        言归正传,在开源作者的指导下,我们又回到问题的本身,既然是参数的问题,那么我们如何来解决呢?找到了正确的方向,接下来就是朝着正确的方向去努力,去找到解决的办法。这里我们按照从简单到复杂,从固化到灵活的三种不同的解决办法,如果还有更好的方法,也欢迎各位专家、博主在评论区指出。

1、加密顺序按实际请求参数求解

        首先分享第一种比较简单的方法,已经知道导致验证失败的原因就是参数不一致的原因。在UniHttp中,使用以下方式定义的接口:

@GetHttpInterface(url="https://api.map.baidu.com/place/v2/search") public HttpResponse<String> getSearch(@QueryPar("query") String query, @QueryPar("region") String region, @QueryPar("output") String output, @QueryPar("scope") String scope, @QueryPar("ret_coordtype") String ret_coordtype, @QueryPar("page_size") int pageSize, @QueryPar("page_num") int pageNum, @QueryPar("ak") String ak, @QueryPar("sn") String sn);

        通常随着参数的定义完成之后,其请求参数的顺序基本就可以预估了,因此第一种方式实际上比较简单,在客户端加密的时候,把参数顺序按照服务端的加密顺序进行处理,服务端的加密加密顺序我们可以使用上一节中的方式进行Debug时进行跟踪。根据请求的参数顺序做如下调整:

@Test public void searchBySn() throws UnsupportedEncodingException { Map<String, String> paramsMap = new LinkedHashMap<String, String>(); String api_prefix = "/place/v2/search?"; /* * - 按请求顺序来加密 */ String output = "json"; String query = "湘菜"; String scope = "2"; int pageNum = 0; String region = "158";// 158表示长沙市 String ret_coordtype = "WGS84"; int pageSize = 20; paramsMap.put("output", output); paramsMap.put("query", query); paramsMap.put("scope", scope); paramsMap.put("page_num", String.valueOf(pageNum)); paramsMap.put("ak", AK_VALUE); paramsMap.put("region", region); paramsMap.put("ret_coordtype", ret_coordtype); paramsMap.put("page_size", String.valueOf(pageSize)); // 调用签名工具生成SN BaiduSignature signature = new BaiduSignature(AK_VALUE, SK_VALUE,paramsMap,api_prefix); String sn = signature.getSnByMap(); System.out.println("sn==>" + sn); HttpResponse<String> result = bdGeoSearchWithSnService.getSearch(query, region, output, scope, ret_coordtype, pageSize, pageNum ,AK_VALUE,sn); System.out.println("第一页的数据如下:"); System.out.println(result); System.out.println(result.getBodyResult()); System.out.println("--------------------------------------------------------------"); }

        再次运行程序发现成功获取了数据:

        上面这种方式虽然可以解决问题,但是每次都需要进行调整参数,使用起来比较麻烦,下面我们根据作者的推荐使用请求前置处理器来进行参数的重新设置。

2、一种兼容Get请求参数动态调整的方法

        关于使用Processor的模式来进行请求参数重置的方法虽然是第一次说,但是之前我们讲过基于Processor的AK自动设置,其核心原理是差不多的。这里我们的改造重点是BaiduHttpApiProcessor中的postBeforeHttpRequest方法。这个方法的核心作用就是发送Http请求之前会回调该方法,可对Http请求体的内容进行二次处理。因此我们首先直接来拼接请求参数的方法。在这里我们需要完全重写请求URL,以完成全新的请求。根据queryParams来重新拼接请求字符串,我们从queryParams中取出请求集合,然后根据Map再设置Url的值,核心方法如下:

/** * * @param snMapParams * @param queryParam * @return */ protected Map<String, Object> reorderingQueryParamMap(String snMapParams,Map<String, Object> queryParam) { Map<String, Object> paramsMap = null; if(StringUtils.isNotEmpty(snMapParams)) { paramsMap = new LinkedHashMap<String, Object>(); //将ak和sn分别放到paramsMap中 paramsMap.put("region", queryParam.get("region")); //其它参数 //将ak和sn分别放到paramsMap中 paramsMap.put("ak", queryParam.get("ak")); paramsMap.put("sn", queryParam.get("sn")); }else { paramsMap = queryParam; } return paramsMap; }

        在请求之前手动修改URL地址,关键方法如下:

/** * -实现-postBeforeHttpMetadata: 发送Http请求之前会回调该方法,可对Http请求体的内容进行二次处理 * * @param uniHttpRequest 原来的请求体 * @param methodInvocation 被代理的方法 * @return 新的请求体 */ @Override public UniHttpRequest postBeforeHttpRequest(UniHttpRequest uniHttpRequest, HttpApiMethodInvocation<BaiduHttpApi> methodInvocation) { /** * -在查询参数中添加提供的appId字段 */ // 获取BaiduHttpApi注解 BaiduHttpApi apiAnnotation = methodInvocation.getProxyApiAnnotation(); // 获取所有查询参数 Map<String, Object> queryParam = uniHttpRequest.getHttpUrl().getQueryParam(); System.out.println("未处理之前的请求参数:" + queryParam); Map<String, Object> paramsMap = this.reorderingQueryParamMap(snMapParams, queryParam); System.out.println("处理之后的请求参数:" + paramsMap); // 第二种方式,直接修改请求参数 String queryString = this.reorderingQueryParam(snMapParams,queryParam); uniHttpRequest.getHttpUrl().setQueryParam(null); String url = uniHttpRequest.getHttpUrl().getUrl() + uniHttpRequest.getHttpUrl().getPath() + "?" + queryString; uniHttpRequest.getHttpUrl().setUrl(url); uniHttpRequest.getHttpUrl().setPath(""); } return uniHttpRequest; }

        请注意,使用这种方式,一般需要将QueryParam参数这是为空,并且完全使用url参数,不要使用path,可以直接设置为null即可。还有使用方式的时候需要注意的点,即我们的接口定义时,不要多加?号,否则会变成双问号,从而导致请求报错,比如:

@GetHttpInterface(path = "/place/v2/search?") public HttpResponse<String> getSearch(@QueryPar("query") String query, @QueryPar("region") String region, @QueryPar("output") String output, @QueryPar("scope") String scope, @QueryPar("ret_coordtype") String ret_coordtype, @QueryPar("page_size") String pageSize, @QueryPar("page_num") String pageNum, @QueryPar("ak") String ak, @QueryPar("sn") String sn);

        加完之后,再查看UniHttp发出的请求地址就会加了一个?号,如下图:

        将请求地址复制出来,

url=https://api.map.baidu.com/place/v2/search??query=%E6%B9%98%E8%8F%9C&region=158&output=json&scope=2&ret_coordtype=WGS84&page_size=20&page_num=0&ak=yourak&sn=d1abf338a49bd82fca6992f5d2b9f686

        由此也会极易导致出现SN校验失败的问题,请大家注意。

3、使用注解来设置重排序规则

        没有什么方法是可以自己来定义请求的请求顺序呢?答案是一定的,我们完全可以将参数的顺序定义到注解中,同时增加一个SN校验的模式开关,这样程序实现起来就更加灵活。在注解类中作如下定义:

package com.yelang.project.thridinterface.apiprocessor; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.core.annotation.AliasFor; import com.burukeyou.uniapi.http.annotation.HttpApi; @Inherited @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @HttpApi(processor = BaiduHttpApiProcessor.class) public @interface BaiduHttpApi { /** * -渠道方域名地址 */ @AliasFor(annotation = HttpApi.class) String url() default "${unihttp.channel.baidu.url}"; /** * -snMapParams 模式下的请求参数顺序,用于按顺序加密,在统一处理器中获取后处理 */ String snMapParams() default ""; /** * - 是否使用sn签名模式 * @return */ boolean snMode() default false; } 

        重点就是这两个参数,通过这两个参数来设置SN模式和具体的参数,定义了注解字后,我们看在Processor中如何使用。简单来说就是先看SN模式,然后再解析参数设置的顺序,再读取参数放置到集合中或者拼接到字符串中。核心方法如下:

/** * * @param snMapParams * @param queryParam * @return */ protected Map<String, Object> reorderingQueryParamMap(String snMapParams,Map<String, Object> queryParam) { Map<String, Object> paramsMap = null; if(StringUtils.isNotEmpty(snMapParams)) { paramsMap = new LinkedHashMap<String, Object>(); //从注解中读取配置的参数 String [] snMap = snMapParams.split(","); //按照参数重新组合参数,生成按顺序的请求字符串 for(String paramKey : snMap) { paramsMap.put(paramKey, queryParam.get(paramKey)); } //将ak和sn分别放到paramsMap中 paramsMap.put("ak", queryParam.get("ak")); paramsMap.put("sn", queryParam.get("sn")); }else { paramsMap = queryParam; } return paramsMap; }

        这样通过结合注解方式来实现了参数顺序的定义,在创建接口时注意注解的设置,实例如下,通过设置参数就设置了运行模式:

@BaiduHttpApi(snMode = true, snMapParams = "query,region,output,scope,ret_coordtype,page_size,page_num") public interface BaiduGeoSearchSnProcessorService { }

4、重写请求的QueryParam重排序方法

这里结合前面的注记方式来对请求参数的重排序进行处理,这种方式适应性更加强。与get方式不同的是,使用设置queryParam的方式,程序的通用性和方法的兼容性会更加好。重新设置QueryParam的方法核心代码如下:

/** * -实现-postBeforeHttpMetadata: 发送Http请求之前会回调该方法,可对Http请求体的内容进行二次处理 * * @param uniHttpRequest 原来的请求体 * @param methodInvocation 被代理的方法 * @return 新的请求体 */ @Override public UniHttpRequest postBeforeHttpRequest(UniHttpRequest uniHttpRequest, HttpApiMethodInvocation<BaiduHttpApi> methodInvocation) { // 获取BaiduHttpApi注解 BaiduHttpApi apiAnnotation = methodInvocation.getProxyApiAnnotation(); String snMapParams = apiAnnotation.snMapParams(); boolean snMode = apiAnnotation.snMode(); //如果开启sn签名,则将请求参数进行充排序后进行签名,反之不用处理 if(snMode) { System.out.println("开启SN签名处理"); // 获取所有查询参数 Map<String, Object> queryParam = uniHttpRequest.getHttpUrl().getQueryParam(); Map<String, Object> paramsMap = this.reorderingQueryParamMap(snMapParams, queryParam); System.out.println("处理之后的请求参数:" + paramsMap); //第一种请求参数重排序 uniHttpRequest.getHttpUrl().setQueryParam(paramsMap); } return uniHttpRequest; }

        经过这样的改造和设计,我们就完美的解决了在UniHttp中请求参数顺序不可控的问题。需要注意的是,参数的加密顺序和请求顺序一致即可。成功请求界面如下:

5、三种方法的使用场景及对比

        以上这三种方法都能满足在UniHttp中实现请求参数动态调整的需求,我们将从代码通用性和改造复杂度来进行对比:

序号处理方式通用性改造复杂度缺点
1客户端兼容需要根据请求参数定制
2兼容Get调参只支持Get方法
3重写QueryParam代码稍微复杂一点,维护难度高

        以上三种思路都能很好的满足业务需要,但是具体采用哪种方式,还需要大家根据自己的场景进行选择。没有最好的架构,只有更好的架构,架构是慢慢演化而来,而不是一蹴而就,良好的架构值得好好学习。

四、总结

        以上就是本文的主要内容,为了解决Java调用UniHttp接口时请求参数乱序导致百度SN签名认证失败的问题。本文将详细探讨这三种解决策略,并通过具体的代码示例和实际操作步骤,帮助开发者理解和应用这些方法。本文的研究意义在于为Java开发者提供一种系统性的解决方案,帮助他们解决在接口调用过程中遇到的请求参数乱序问题。通过深入分析问题的根源,并提出有效的解决策略,本文旨在提高接口调用的成功率,减少开发过程中的错误和风险。同时,本文的研究成果也可以为其他使用类似接口调用框架和签名认证机制的开发者提供参考和借鉴。博文由浅入深,按照问题重现、查找原因、问题突破的方式对相关内容进行讲解,希望大家都能掌握着三种不同的处理方式。行文仓促,定有不足之处,欢迎各位朋友在评论区批评指正,不胜感激。

Read more

【VLM】Qwen3-VL模型架构和训练流程

【VLM】Qwen3-VL模型架构和训练流程

note * Qwen3-VL模型,提供稠密型(2B/4B/8B/32B)和混合专家型(30B-A3B/235B-A22B)两种变体。通过集成高质量的多元模态数据迭代和架构创新(如增强的交错MRoPE、DeepStack视觉-语言对齐和基于文本的时间对齐) * 其原生支持256K token的交错序列,使其能够在长复杂文档、图像序列和视频上进行稳健的推理,特别适用于现实世界应用中高保真跨模态理解的需求。Qwen3-VL系列的密集和MoE变体确保了在不同延迟和质量要求下的灵活部署,后训练策略包括非思考模式和思考模式,进一步提升了模型的应用范围。 * 数据过滤方面,去除噪声、低对齐样本,确保数据质量与多样性。 * 模型架构方面,使用DeepStack 跨层融合,提取视觉编码器多中间层特征,通过轻量残差连接注入 LLM 对应层,强化视觉-语言对齐,保留从低级到高级的丰富视觉信息。 * RoPE旋转位置编码的高低频含义: * 低频:转得慢,擅长远距离位置区分(长序列、大图、长视频等) * 高频:转得快,位置稍微一变,角度就剧变,擅长近距离精细区分(小区域、局部细

By Ne0inhk
Docker 安装 OpenClaw 报错排查:如何解决Gateway auth is set to token, but no token is configured``Missing config

Docker 安装 OpenClaw 报错排查:如何解决Gateway auth is set to token, but no token is configured``Missing config

Docker 安装 OpenClaw 报错排查:如何解决Gateway auth is set to token, but no token is configured``Missing config. Run openclaw setup``control ui requires HTTPS or localhost``Proxy headers detected from untrusted address 按错误关键词 Ctrl+F 秒搜定位,建议收藏备用! 文章目录 * Docker 安装 OpenClaw 报错排查:如何解决`Gateway auth is set to token, but

By Ne0inhk
SpringAI 大模型应用开发篇-SpringAI 项目的新手入门知识

SpringAI 大模型应用开发篇-SpringAI 项目的新手入门知识

🔥博客主页: 【小扳_-ZEEKLOG博客】 ❤感谢大家点赞👍收藏⭐评论✍ 文章目录         1.0 SpringAI 概述         1.1 大模型的使用         2.0 SpringAI 新手入门         2.1 配置 pom.xml 文件         2.2 配置 application.yaml 文件         2.3 配置 ChatClient         2.4 同步调用         2.5 流式调用         2.6 System 设定         2.7 日志功能         2.8 会话记忆功能

By Ne0inhk