Java 调用高德地图Sig签名遇10007 INVALID_USER_SIGNATURE的解决之道

Java 调用高德地图Sig签名遇10007 INVALID_USER_SIGNATURE的解决之道

目录

前言

一、如何开启高德的数字签名

1、应用配置

2、官方的生成机制

二、Java集成UniHttp

1、UniHttp接口定义

2、非SIG验证访问

3、高德数字签名的实现

三、常见问题及解决办法

1、编程式参数顺序设置

2、参数重排序设置

3、特殊字符的处理

四、高德地图与百度地图数字签名对比

五、总结


前言

        在现代软件开发中,地图服务的集成已成为众多应用的必备功能之一。大家日常使用频率较高的除了百度地图之外,高德地图也是其中的重要服务提供商,其丰富的 API 接口为开发者提供了便利。然而,在使用 Java 调用高德地图服务时,如果开发者开启了数字签名的机制,可能会遇到各种问题,其中最常见的便是 10007 INVALID_USER_SIGNATURE 错误。这一错误表明数字签名未通过验证,导致无法正常访问高德地图的服务。

        在之前的博客中,我曾经对百度地图的SN数字签名机制进行了详细的讲解,包括基于Java的原生请求机制和基于UniHttp的封装请求机制,原文链接如下:

序号博客地址
1Java 开发者如何搞定百度地图 SN 权限签名实践-以搜索2.0接口为例
2Java调用UniHttp接口请求失败?一次开源的深入实践-百度SN签名认证场景下参数乱序问题的三种解决策略

        很多朋友反馈对百度地图的数字签名机制介绍很详细,能不能对高德地图的数字签名机制也进行介绍介绍。众所周知,数字签名机制是为了确保请求的安全性和合法性而设计的。当开发者在开放平台的控制台中开启了数字签名功能后,就需要按照指定的算法生成数字签名。如果签名生成过程中出现任何问题,如参数错误、算法不匹配或密钥错误等,都会导致请求错误,因此数字签名机制其实是一种安全保障机制。虽然之前对百度地图的数字签名机制有一定的了解,本来以为高德验证机制比较跟百度的类似,转换使用起来应该非常块,实践证明。相似性和直接照搬还是不同的,“纸上得来终觉浅,绝知此事要躬行”。两个开放平台之间还是有一定的差别的。

        本文即在此背景下产生,文章主要讲解如何在高德地图中开启SIG数字签名,开启数字签名后可能会遇到什么问题,然后介绍使用Java进行调用时可能遇到的场景以及如何解决调用时返回"info":"INVALID_USER_SIGNATURE","infocode":"10007"的错误。通过这些内容的展开,开发者可以有效地解决 Java 调用高德地图服务时遇到的 10007 INVALID_USER_SIGNATURE 错误,同时由于高德地图在官网上也没有提供示例代码,因此本文也算是一种补充,让各位开发者可以直接参考使用。

一、如何开启高德的数字签名

        本节将简单介绍如何在高德地图中开启数字签名的应用配置和官方的生成机制介绍。

1、应用配置

        进入控制台-应用管理-我的应用。

        1)新建Key/选择相应的Key。

        2)点击“设置”,打开数字签名,提交后关闭“设置”页面

        (注意:该私钥与Key对应,请注意保存,谨防泄露。)

2、官方的生成机制

        我们可以查看官方提供的数字签名生成机制说明网页链接,如下图所示:

        从官方的说明可以知晓:

生成签名

签名格式:sig=MD5(请求参数(包括key)键值对(按参数名的升序排序),加(请注意“加”字无需输入)私钥)。例如:

请求服务为“testservice”;请求参数分别为“a=23,b=12,d=48,f=8,c=67”;私钥为“bbbbb”。则数字签名为:sig=md5(a=23&b=12&c=67&d=48&f=8bbbbb)

注意:
生成签名的内容,(上文提到的拼装的参数,也就是md5()中的内容),必须为utf-8编码格式。在计算md5的参数如果出现+号,请正常计算sig,但在请求的时候,需要用urlencode进行编码再请求。请求参数排序需要注意,如果参数名的第一个字母顺序相同,就比较第二个字母。以此类推,直至得到排序结果。

在请求中添加签名

将签名sig作为参数添加至请求参数中:参数名为sig,值为根据请求参数与私钥计算出的值。

二、Java集成UniHttp

        本节将以UniHttp为例,详细讲解如何在Java中集成UniHttp,并且使用Java实现非Sig验证的接口访问以及如何定义高德数字签名工具类。通过本节可以了解如何在UniHttp中进行接口的定义以及高德数字签名的具体实现。

1、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(url = "https://restapi.amap.com/v3/geocode") public interface AmapGeocodeWithSigService { /** * - 带sig签名的地理编码API服务 * @param address address * @param city city * @param key key * @param sig 经过计算加密的sig * @return */ @GetHttpInterface("/geo") public HttpResponse<String> getGeoWithSig(@QueryPar("address") String address,@QueryPar("city") String city, @QueryPar("key") String key, @QueryPar("sig") String sig); }

2、非SIG验证访问

        为了让大家理解数字签名机制,这里我们简单介绍一下非SIG验证访问。即知道key就可以成功调用接口。简单方法如下:

package com.yelang.project.unihttp; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.LinkedHashMap; 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.common.utils.StringUtils; import com.yelang.project.thridinterface.AmapGeocodeService; import com.yelang.project.thridinterface.AmapGeocodeWithSigService; import com.yelang.project.thridinterface.signature.AmapSignature; @SpringBootTest @RunWith(SpringRunner.class) public class AmapGeoSearchBySigCase { private static final String AMAP_CLIENT_NOSIG_AK = "youramapkey"; @Autowired private AmapGeocodeService geocodeService;//地理和逆地理编码 @Test public void geoWithNoSig() throws InterruptedException { String address = "湖南大学"; String city = "430100";//长沙 String key = AMAP_CLIENT_NOSIG_AK; //step1、调用高德地理编码接口将地址转为经纬度 HttpResponse<String> result = geocodeService.getGeo(address, city, key); if(StringUtils.isNotEmpty(result.getBodyResult())) { System.out.println(result.getBodyResult()); } } }

        在控制台中运行以上程序后,在控制台中可以看到以下输出,说明接口成功调用。

3、高德数字签名的实现

        根据官网文档里面介绍的数字签名的实现方式,我们新建一个高德地图的数字签名生成器,能够自动处理数据,并生成正确的数字签名。核心代码如下:

package com.yelang.project.thridinterface.signature; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; /** * - 高德 signature签名 * @author 夜郎king */ public class AmapSignature { /** * -应用申请的AK值 */ private String akValue; /** * - 应用申请的SK值 */ private String skValue; /** * - Map参数集合 */ private Map<String, ?> data; /** * - API接口访问前缀 */ private String apiPrefix; public String getAkValue() { return akValue; } public void setAkValue(String akValue) { this.akValue = akValue; } public String getSkValue() { return skValue; } public void setSkValue(String skValue) { this.skValue = skValue; } public Map<String, ?> getData() { return data; } public void setData(Map<String, ?> data) { this.data = data; } public String getApiPrefix() { return apiPrefix; } public void setApiPrefix(String apiPrefix) { this.apiPrefix = apiPrefix; } public AmapSignature() { super(); } public AmapSignature(String akValue, String skValue, Map<String, ?> data) { super(); this.akValue = akValue; this.skValue = skValue; this.data = data; } //来自stackoverflow的MD5计算方法,调用了MessageDigest库函数,并把byte数组结果转换成16进制 private String MD5(String md5) { try { java.security.MessageDigest md = java.security.MessageDigest.getInstance("MD5"); byte[] array = md.digest(md5.getBytes()); StringBuffer sb = new StringBuffer(); for (int i = 0; i < array.length; ++i) { sb.append(Integer.toHexString((array[i] & 0xFF) | 0x100).substring(1, 3)); } return sb.toString(); } catch (java.security.NoSuchAlgorithmException e) { } return null; } /** * 生成签名 * * @param reOrder 重排序 * @return 签名 */ public String generateSignature(boolean reOrder) { // 参数排序 List<String> keys = new ArrayList<>(this.data.keySet()); // 需要重排序 if (reOrder) { Collections.sort(keys); } // 拼接参数 StringBuilder sb = new StringBuilder(); for (String key : keys) { sb.append(key).append("=").append(this.data.get(key)).append("&"); } System.out.println("其它md5求解:" + this.MD5(sb.substring(0, sb.length() - 1) + this.skValue)); // 计算MD5 return MD5(sb.substring(0, sb.length() - 1) + this.skValue); } }

        重排序的设置非常重要,主要是为了解决参数乱序时导致的请求数字签名不一致的问题。在下文中会详细涉及。

三、常见问题及解决办法

        使用Java定义了高德地图的数字签名的具体实现后,下面我们就来使用三个真实的场景进行测试,看看会遇到什么问题以及我们是如何解决的。通过这几个问题的解决,就可以掌握在实际开发中容易遇到的问题。

1、编程式参数顺序设置

        其实不管是百度地图还是高德地图,他们的数字签名的认证设计思想都比较相同,尤其是加密时的参数顺序,一定是不能错的,一旦有的参数顺序不对,那么最终加密得到的结果已经是错的。因此保证请求参数顺序非常重要。保证参数顺序的方式有很多种,首先我们来讲解第一种方式编程式参数顺序设置,顾名思义就是使用代码的方式来指定参数顺序。在Java当中,我们可以使用LinkedHashMap或者TreeMap来进行参数的设置,都是可以的。只是使用TreeMap默认就是按照字符来进行排序的。两者相差不大,我们以地理编码接口为例:

@Test public void getWithSig() { String city = "430100";//长沙 String key = AMAP_CLIENT_SIG_AK; String address = "岳麓山风景区"; String secret_key = AMAP_CLIENT_SECRET_KEY; LinkedHashMap<String, String> data = new LinkedHashMap<String, String>(); data.put("address", address); data.put("city", city); data.put("key", key); System.out.println(data); AmapSignature amapSignature = new AmapSignature(key, secret_key, data); String sig = amapSignature.generateSignature(false); System.out.println("sig==" + sig); //step1、调用高德地理编码接口将地址转为经纬度 HttpResponse<String> result = geoCodeWithSigService.getGeoWithSig(address, city, key,sig); System.out.println("开始执行"); if(StringUtils.isNotEmpty(result.getBodyResult())) { System.out.println(result.getBodyResult()); } }

        这里的参数我们按照参数的key的值进行了手动排序,在程序中我们设置不需要参数排序,

String sig = amapSignature.generateSignature(false);

        来看一下程序的运行结果:

        我们随便改变参数的顺序,比如把key放到最前面去,在次运行结果如下:

2、参数重排序设置

        编程式参数设置严重的依赖开发工程师,而且极易被忘记,因此我们在生成sig时直接对参数进行重排序,然后按照重排序的方式进行处理即可。重排序的方法很简单,创建sig时传入计算参数即可:

String sig = amapSignature.generateSignature(true);

        传入参数后,计算的参数的函数逻辑如下:

        这里会对keys这个集合进行重排序,这样就得到了参数顺序统一化的处理。在此运行后,无论前面的集合使用什么类型,哪怕是HashMap都没有问题,因为在生成数字签名时进行了统一排序就保证了正确性。

3、特殊字符的处理

        在官网的手册上,有一个关于特殊字符的说明:

在计算md5的参数如果出现+号,请正常计算sig,但在请求的时候,需要用urlencode进行编码再请求。

        这里的+号只是一个符号,还有可能有其它的符号,可以看下在查询字符串中增加特殊符号后,会报什么错误?

        有了特殊字符后又会报这个10007的错误,在上面的提示中已经包含了解决方法,sig计算正常计算数字签名,但是请求时需要编码。编码的核心方法如下:

//step2、对包含特殊字符的进行值重编码 address = URLEncoder.encode(address, "UTF-8");

        其它没有特殊字符的值可以不做处理,再次运行后在控制台可以看到接口访问正常了。

四、高德地图与百度地图数字签名对比

        这里简单对两个平台的数字签名进行一个简单的对比。

百度地图高德地图
请求前缀是否带入计算
请求参数可否乱序
编码复杂度
是否包含示例

五、总结

        以上就是本文的主要内容,文章主要讲解如何在高德地图中开启SIG数字签名,开启数字签名后可能会遇到什么问题,然后介绍使用Java进行调用时可能遇到的场景以及如何解决调用时返回"info":"INVALID_USER_SIGNATURE","infocode":"10007"的错误。通过详细讲解如何在Java中集成UniHttp,集成UniHttp之后可能会碰到什么问题,最后通过问题的解决积累开发经验,同时对百度地图和高德地图的数字签名方式进行了简单的对比,为大家实际开发时做一个简单的参考。行文仓促,定有许多的不足之处,欢迎各位朋友在评论区批评指正,不胜感激。

Read more

全网最全100道C++高频经典面试题及答案解析:C++程序员面试题库分类总结

全网最全100道C++高频经典面试题及答案解析:C++程序员面试题库分类总结

前言 C++作为一门兼具高性能与灵活性的语言,持续推动着量子计算、自动驾驶、区块链、AI编译器等领域的技术革命。本题库精选100道高频面试题,涵盖从内存模型、编译器内部机制到跨学科前沿应用的深度内容,专为资深工程师、系统架构师及科研岗位设计。无论是准备顶级科技公司面试,还是探索C++在安全关键系统(如航天、医疗)与新兴领域(如脑机接口、边缘AI)的工程实践,这些题目将帮助您展现对语言本质的理解和对复杂场景的掌控力。 题库特点: 垂直深入:超越语法层面,聚焦标准演进(C++20/23)、硬件协同优化及形式化验证等高级主题。 跨领域融合:结合LLVM/MLIR编译器开发、CUDA加速、实时操作系统等场景,体现C++的系统级控制能力。 第一部分:面向对象与内存管理(1-10题) 1. 虚函数实现原理(字节跳动/腾讯) 题目:虚函数表(vtable)在C++中是如何工作的?写出示例代码说明动态多态的实现。

By Ne0inhk

3.6-Web后端基础(java操作数据库)

目录 前言 JDBC 介绍 查询数据 需求 准备工作 代码实现 代码剖析 ResultSet 预编译SQL SQL注入 SQL注入解决 性能更高 增删改数据 需求 代码实现 Mybatis 介绍 快速入门 辅助配置 配置SQL提示 配置Mybatis日志输出 JDBC VS Mybatis 数据库连接池 介绍 产品 增删改查操作 删除 新增 修改 查询 XML映射配置 XML配置文件规范 XML配置文件实现 MybatisX的使用 SpringBoot配置文件 介绍 语法 案例 前言 在前面我们学习MySQL数据库时,都是利用图形化客户端工具(如:idea、datagrip),来操作数据库的。 我们做为后端程序开发人员,

By Ne0inhk
SkyWalking - .NET / C++ / Lua 探针现状与社区支持

SkyWalking - .NET / C++ / Lua 探针现状与社区支持

👋 大家好,欢迎来到我的技术博客! 📚 在这里,我会分享学习笔记、实战经验与技术思考,力求用简单的方式讲清楚复杂的问题。 🎯 本文将围绕SkyWalking这个话题展开,希望能为你带来一些启发或实用的参考。 🌱 无论你是刚入门的新手,还是正在进阶的开发者,希望你都能有所收获! 文章目录 * SkyWalking - .NET / C++ / Lua 探针现状与社区支持 🌐 * 一、SkyWalking 多语言探针架构概览 🧩 * 二、Java 探针:成熟稳定,功能最全 ☕️ * 示例:Spring Boot 应用接入 SkyWalking * Java 探针高级特性 * 三、.NET 探针现状:渐趋成熟,生产可用 🖥️ * 技术原理 * 使用方式 * 当前支持的功能 * 局限性 * 四、C++ 探针现状:SDK 形式,适合嵌入式场景 ⚙️ * cpp2sky SDK

By Ne0inhk

从“会聊天”到“会交付”:用 OpenClaw + DeepSeek 做一个可落地的 AI Agent 工程化流水线(Java/Go/Python)

从“会聊天”到“会交付”:用 OpenClaw + DeepSeek 做一个可落地的 AI Agent 工程化流水线(Java/Go/Python) 主品牌:王仕宇(JavaPub) 关键词:OpenClaw、DeepSeek、AI Agent、大模型工程化、AI Coding、面试提效 一、今天的行业信号:Agent 正在从 Demo 走向交付 过去一年,大家都在讨论“AI 会不会替代程序员”。到 2026 年,一个更务实的问题已经出现: 你的 Agent,能不能稳定、可观测、可复用地交付结果? 这背后不是模型参数竞赛,而是工程化能力竞赛: * 任务编排是否可控(Cron / Heartbeat

By Ne0inhk