springboot 腾讯地图接口验签 java

news2024/11/25 15:37:06

1. 原因

  • 需求需要通过小程序定位拿到用户所在行政区信息,但是小程序定位只能拿到经纬度信息,所以需要调用腾讯地图的逆地址解析(我认为:微信是腾讯的,那么使用腾讯地图的逆地址解析经度应该不会损失太多)
  • 如果WebServiceAPI Key配置中签名校验,那么调用接口就需要进行验签

image.png

2. WebServiceAPI(GET方法)签名计算

官方文档地址:以下内容是从官方文档摘写下来的验签规则
(1)通用概念:
a. 请求路径:调用接口时的路径,如:/ws/geocoder/v1,末尾是否带 / 均可,不做要求,但需要保持一致,比如调用路径用了/ws/geocoder/v1,签名计算的时候也要用/ws/geocoder/v1
b. SecretKey (SK):在腾讯位置服务控制台 > Key配置中,勾选WebServiceAPI的 SN校验时自动生成的随机字串,用于计算签名(sig)
c. sig:签名计算结果
通过以下示例讲解(本例为调用逆地址解析请求的url):
https://apis.map.qq.com/ws/geocoder/v1?location=28.7033487,115.8660847&key=5Q5BZ-5EVWJ-SN5F3-K6QBZ-B3FAO-*****

(2)GET请求分为:域名,请求路径和参数三个部分,用于签名计算的有:
请求路径:/ws/geocoder/v1
请求参数:location=28.7033487,115.8660847&key=5Q5BZ-5EVWJ-SN5F3-K6QBZ-B3FAO-*****
a. 首先对参数进行排序:按参数名升序(本例结果为key在前,location在后):

key=5Q5BZ-5EVWJ-SN5F3-K6QBZ-B3FAO-*****&location=28.7033487,115.8660847

b. 签名计算(sig):
请求路径+”?”+请求参数+SK进行拼接,并计算拼接后字符串md5值(字符必须为小写),即为签名(sig):
要求:请求参数必须是未进行任何编码(如urlencode)的原始数据

md5("/ws/geocoder/v1?key=5Q5BZ-5EVWJ-SN5F3-*****&location=28.7033487,115.8660847SWvT26ypwq5Nwb5RvS8cLi6NSoH8HlJX")

本例计算得到结果为:90da272bfa19122547298e2b0bcc0e50
c. 生成最终请求:将计算得到的签名sig,放到请求中(参数名即为:sig):

https://apis.map.qq.com/ws/geocoder/v1?key=5Q5BZ-5EVWJ-SN5F3-K6QBZ-B3FAO-*****&location=28.7033487,115.8660847&sig=90da272bfa19122547298e2b0bcc0e50

注意:计算 sig 要使用原始参数值,不要进行任何编码,但最终发送时的参数,是需要对接口传入的参数值做url编码的

url编码方式:

以地点检索接口位例:
 
请求:...域名省略.../place/v1/search?boundary=region(北京)&keyword=美食

错误方式:"...域名省略.../place/v1/search?"+urlencode("boundary=region(北京)&keyword=美食")
 
正确方式:"...域名省略.../place/v1/search?boundary="+urlencode("region(北京)")+"&keyword="+urlencode("美食")

注:示例中的urlencode()代表url编码函数,不同开发语言的存在不同,以您实际为准

3. 腾讯地图java验签工具类

常量信息

  • KEY和SK自己去后台查看(我没有封装为配置,有需要的可以自行封装)
  • 需要新增接口就在这个常量中新增,然后在工具类中引用就行
package com.applets.manager.core.constant;

/**
 * @author zr 2024/4/26
 */
public class TencentMapConstant {
    /** KEY */
    public static final String KEY = "xxxxx-xxxxx-xxxxx-xxxxx-xxxxx-QGBVW";
    /** SecretKey (SK) */
    public static final String SK = "xxxxxxxxxxxxxxxxxxxxxxxx";
    /** 域名 */
    public static final String HOST = "https://apis.map.qq.com";
    /** 逆地址解析api */
    public static final String GEOCODER_API = "/ws/geocoder/v1";
//    /** 距离矩阵 */
//    public static final String DISTANCE_API = "/ws/distance/v1/matrix";
//    /** IP定位 */
//    public static final String IP_LOCATION_API = "/ws/location/v1/ip";
}

工具类

  • 附带get接口验签和post接口验签
package com.applets.manager.core.util;

import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONObject;
import com.applets.manager.core.constant.TencentMapConstant;
import lombok.extern.slf4j.Slf4j;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;

/**
 * @author zr 2024/4/26
 */
@Slf4j
public class TencentMapUtil {
    /**
     * 签名计算(sig)_GET:
     * @param path  请求路径
     * @param params    请求参数
     * @return
     */
    public static String generateSignatureGet(String path, Map<String, String> params) {
        try {
            //将参数按照 key 进行字典序排序
            TreeMap<String, String> sortedParams = new TreeMap<>(params);
            //构建原始签名字符串
            StringBuilder rawSignatureBuilder = new StringBuilder();
            rawSignatureBuilder.append(path).append("?");
            int entryIndex = 0;
            for (Map.Entry<String, String> entry : sortedParams.entrySet()) {
                rawSignatureBuilder.append(entry.getKey()).append("=").append(entry.getValue());
                if (entryIndex < sortedParams.size() - 1) {
                    rawSignatureBuilder.append("&");
                }
                entryIndex++;
            }
            rawSignatureBuilder.append(TencentMapConstant.SK);
            String rawSignature = rawSignatureBuilder.toString();

            // 计算 MD5 签名
            byte[] signatureBytes = MessageDigest.getInstance("MD5").digest(rawSignature.getBytes("UTF-8"));
            String signature = byteArrayToHexString(signatureBytes);

            return URLEncoder.encode(signature, "UTF-8"); // 进行 URL 编码
        } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
            log.error("签名计算失败: {}", e.getMessage(), e);
            return null;
        }
    }
    private static String byteArrayToHexString(byte[] bytes) {
        StringBuilder hexString = new StringBuilder();
        for (byte b : bytes) {
            hexString.append(String.format("%02x", b));
        }
        return hexString.toString();
    }

    /**
     * 对参数进行编码
     * @param params
     * @return
     */
    public static String encodeParams(Map<String, String> params) {
        TreeMap<String, String> sortedParams = new TreeMap<>(params);
        StringBuilder rawSignatureBuilder = new StringBuilder();
        for (Map.Entry<String, String> entry : sortedParams.entrySet()) {
            rawSignatureBuilder.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
        }
        return rawSignatureBuilder.toString();
    }

    /**
     * 签名计算(sig)_POST:
     * @param jsonObject
     */
    public static String generateSignaturePost(String path, JSONObject jsonObject) {
        //一级属性名排序字符升序排序
        Set<String> propertyNames = jsonObject.keySet();
        List<String> sortedPropertyNames = new ArrayList<>(propertyNames);
        Collections.sort(sortedPropertyNames);
        JSONObject sortedJsonObject = new JSONObject();
        for (String propertyName : sortedPropertyNames) {
            //  Value转成JSON string
            sortedJsonObject.put(propertyName, JSONUtil.toJsonStr(jsonObject.get(propertyName)));
        }
        //替换原始的 JSONObject
        jsonObject.clear();
        jsonObject.putAll(sortedJsonObject);

        //拼接成rawSignatureBuilder型的字符串
        StringBuilder rawSignatureBuilder = new StringBuilder();
        for (Map.Entry<String, Object> entry : jsonObject.entrySet()) {
            String propertyName = entry.getKey();
            String propertyValue = (String) entry.getValue();
            // 拼接成 rawSignatureBuilder 型的字符串
            rawSignatureBuilder.append(propertyName)
                    .append("=")
                    .append(propertyValue)
                    .append("&");
        }
        //去除末尾的"&"
        if (rawSignatureBuilder.length() > 0) {
            rawSignatureBuilder.deleteCharAt(rawSignatureBuilder.length() - 1);
        }

        //签名计算(sig) 请求路径+”?”+请求参数+SK
        rawSignatureBuilder.insert(0, path + "?").append(TencentMapConstant.SK);
        String rawSignature = rawSignatureBuilder.toString();

        // 计算 MD5 签名
        try {
            byte[] signatureBytes = MessageDigest.getInstance("MD5").digest(rawSignature.getBytes("UTF-8"));
            String signature = byteArrayToHexString(signatureBytes);

            return URLEncoder.encode(signature, "UTF-8"); // 进行 URL 编码
        } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
            log.error("签名计算失败: {}", e.getMessage(), e);
        }
        return null;
    }
}

4. 测试

package com.applets.manager.core;

import cn.hutool.core.map.MapUtil;
import com.alibaba.fastjson.JSON;
import com.applets.manager.core.constant.TencentMapConstant;
import com.applets.manager.core.util.TencentMapUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.client.RestTemplate;
import java.util.*;

/**
 * @author zr 2024/4/26
 */
@RunWith(SpringRunner.class)
//@SpringBootTest(classes = StartApplication.class)
@Slf4j
public class TencentTest {

    @Test
    public void getTest() {
        /** get请求示例 */
        Map<String, String> params = MapUtil.builder(new HashMap<String, String>())
                .put("location", "28.7033487,115.8660847")
                .put("key", TencentMapConstant.KEY)
                .build();
        // 生成最终请求
        String signature = TencentMapUtil.generateSignatureGet(TencentMapConstant.GEOCODER_API, params);
        String finalRequest = TencentMapConstant.HOST + TencentMapConstant.GEOCODER_API + "?" + TencentMapUtil.encodeParams(params) + "sig=" + signature;
        System.out.println(finalRequest);
        RestTemplate restTemplate = new RestTemplate();
        TencentMapResult forObject = restTemplate.getForObject(finalRequest, TencentMapResult.class);
        log.info("地址:{}", JSON.toJSONString(forObject));

    }

    @Test
    public void postTest() {
//        /** post请求示例 */
//        JSONObject requestData = new JSONObject();
//        requestData.set("mode", "driving");
//        requestData.set("key", TencentMapConstant.KEY);
//        requestData.set("from", "32.139063,118.724270");
//        requestData.set("to", "32.150763,118.734398;32.157158,118.696632");
//
//        String signature = TencentMapUtil.generateSignaturePost(TencentMapConstant.DISTANCE_API, requestData);
//        String finalRequest = TencentMapConstant.HOST + TencentMapConstant.DISTANCE_API + "?mode=driving" + "&sig=" + signature;
//        System.out.println(finalRequest);
//        RestTemplate restTemplate = new RestTemplate();
//        System.out.println(restTemplate.postForObject(finalRequest, requestData, TencentMapResult.class));
    }

    @Data
    public static class TencentMapResult<T> {
        /**
         * 状态码,0为正常,其它为异常
         */
        private Integer status;

        /**
         * 状态说明
         */
        private String message;

        /**
         * 本次请求的唯一标识
         */
        private String request_id;

        /**
         * 查询结果总数量
         */
        private Integer count;

        /**
         * 返回数据 > 数组
         */
        private T data;

        /**
         * 返回数据 > 对象
         */
        private T result;

    }
}

测试结果
image.png

因为我这里只开通了一个get接口的使用权限,所以post的就不测了,测试方法在TencentTest.postTest(),需要将TencentMapConstant.DISTANCE_API换成自己的即可

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1832901.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

YOLOv8 + SAM实现自动标注分割数据集【附完整源码+步骤详解】

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推…

eclipse maven打包报错: 致命错误: 在类路径或引导类路径中找不到程序包 java.lang的解决

还是上来帖张图&#xff1a; 1、系统之前是运行在mac上的&#xff0c;打包一切正常&#xff0c;但是现在在win11的eclipse打包就报错了。 2、致命错误: 在类路径或引导类路径中找不到程序包 java.lang&#xff0c;上面的问题应该是找不到java中的jar中的class导致。 解决&…

会声会影2023软件怎么下载安装? 【详细安装图文教程】

简介&#xff1a; 会声会影&#xff08;Corel VideoStudio&#xff09;为加拿大Corel公司发布的一款功能丰富的视频编辑软件。会声会影2023简单易用&#xff0c;具有史无前例的强大功能&#xff0c;拖放式标题、转场、覆叠和滤镜&#xff0c;色彩分级、动态分屏视频和新增强的…

小米充电宝怎么样?西圣、小米、罗马仕充电宝测评谁是卷王!

充电宝说实话在我们日常生活中还是非常常见的一个出门必备的充电设备&#xff0c;除了出门必须带数据线之外&#xff0c;充电宝也是不例外的&#xff0c;对于手机不耐电的朋友来说在外面有一个充电宝简直就是蓄电“救星”&#xff0c;什么都可以不带但是充电宝是必带的一款装备…

苍穹外卖笔记-18-修改密码、bug记录

文章目录 1 修改密码1.1 需求分析和设计1.2 代码实现1.2.1 admin/EmployeeController1.2.2 EmployeeService1.2.3 EmployeeServiceImpl 1.3 功能测试 2 bug记录 1 修改密码 完结的时候发现还有一个接口未实现。这里补充 1.1 需求分析和设计 产品原型&#xff1a; 业务规则&am…

海南聚广众达电子商务咨询有限公司引领行业变革

在数字化浪潮席卷全球的今天&#xff0c;电商行业正以前所未有的速度发展。海南聚广众达电子商务咨询有限公司&#xff0c;凭借其在抖音电商领域的深厚积累和不断创新&#xff0c;正逐步成为行业的佼佼者。这家以专注、专业、专注为核心理念的公司&#xff0c;不仅为客户提供全…

GenICam标准(二)

系列文章目录 GenICam标准&#xff08;一&#xff09; GenICam标准&#xff08;二&#xff09; GenICam标准&#xff08;三&#xff09; GenICam标准&#xff08;四&#xff09; GenICam标准&#xff08;五&#xff09; GenICam标准&#xff08;六&#xff09; 文章目录 系列文…

前端锚点 点击 滑动双向绑定

一. 页面样式 二. 代码 <div class"flexBox"><div class"mdDiv" v-for"(item,index) in tabList" :key"index" :class"nowChooseindex?choosed:" click"jumpMD(index, item.id)">{{item.name}}&l…

找工作小项目:day16-重构核心库、使用智能指针(2)

day16-重构核心库、使用智能指针 太多了分一篇写。 5、EventLoop 这是一个事件轮询&#xff0c;在这个部分会通过Poller进行就绪事件的获取&#xff0c;并将事件进行处理。 头文件 这里使用了一个智能指针并使用的是unique_ptr指向Poller红黑树&#xff0c;防止所有权不止…

应变玻璃合金是航天产业重要弹性材料 研究开发意义重大

应变玻璃合金是航天产业重要弹性材料 研究开发意义重大 应变玻璃&#xff0c;是一种形状记忆合金&#xff0c;为纳米级材料&#xff0c;其短程有序晶格应变区域呈冻结状态&#xff0c;具有典型的玻璃化转变特征&#xff0c;可以对外界刺激产生应变反应&#xff0c;也称为应变玻…

【Kaggle量化比赛】Top讨论

问: 惊人的单模型得分,请问您使用了多少个特征来获得如此高的得分?我也在使用LGB模型。 答 235个特征(180个基本特征+滚动特征) 问: 您是在使用Polars进行特征工程还是仅依赖于Pandas+Numba/多进程?即使进行了Numba优化,我也发现当滚动特征过多时,推理速度会非常慢。在…

云服务器ECS是什么?云服务器ECS有什么方便之处

什么是云服务器ECS&#xff1f; 云服务器 ECS 本质上是一种基于云计算技术的虚拟服务器。与传统服务器不同&#xff0c;它打破了传统物理服务器的局限性&#xff0c;为我们提供了一种全新的计算资源使用方式。 我们不再需要购买和维护昂贵的硬件设备&#xff0c;而是可以根据…

C++新特性复习1 版本11

参照来自于&#xff1a; cppreference.com 老实说&#xff0c;我是毕业不久就开始用C&#xff0c;原因就是VC&#xff0c;当时用来做界面。还好吧&#xff0c;不是觉得太难&#xff0c;起码对数学底子没有要求&#xff0c;后面偶尔也用用&#xff0c;但是整体还是C居多。现在项…

msvcr120.dll是干嘛的,找不到msvcr120.dll无法执行如何解决方法

在计算机使用过程中&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中之一就是“msvcr120.dll丢失”。那么&#xff0c;msvcr120.dll到底是什么&#xff1f;它为什么会丢失&#xff1f;丢失后会对电脑产生什么影响&#xff1f;如何解决这个问题&#xff1f;本文将为您详…

纷享销客PaaS平台基础能力:一文说清 “业务定制能力”

01、业务对象定制能力 一个优秀的PaaS(平台即服务)平台的业务对象定制能力应该具备以下特点&#xff1a; 敏捷的业务模型&#xff1a; 能够根据用户的业务需求&#xff0c;提供可定制的数据模型和数据处理能力&#xff0c;支持各种数据类型和数据操作。 可视化的界面定制能力…

长短期记忆神经网络(LSTM)的回归预测(免费完整源代码)【MATLAB】

LSTM&#xff08;Long Short-Term Memory&#xff0c;长短期记忆网络&#xff09;是一种特殊类型的递归神经网络&#xff08;RNN&#xff09;&#xff0c;专门用于处理和预测基于时间序列的数据。与传统RNN相比&#xff0c;LSTM在处理长期依赖问题时具有显著优势。 LSTM的基本…

聊聊DoIP吧(三)-端口号port

DoIP在UDP和TCP建立连接和发送诊断报文的过程中使用的端口定义如下&#xff1a;

springboot宠物领养系统-计算机毕业设计源码07863

摘 要 21世纪的今天&#xff0c;随着社会的不断发展与进步&#xff0c;人们对于信息科学化的认识&#xff0c;已由低层次向高层次发展&#xff0c;由原来的感性认识向理性认识提高&#xff0c;管理工作的重要性已逐渐被人们所认识&#xff0c;科学化的管理&#xff0c;使信息存…

Linux C语言:字符串处理函数

一、字符串函数 1、C库中实现了很多字符串处理函数 #include <string.h> ① 求字符串长度的函数strlen② 字符串拷贝函数strcpy③ 字符串连接函数strcat④ 字符串比较函数strcmp 2、字符串长度函数strlen 格式&#xff1a;strlen(字符数组)功能&#xff1a;计算字符串…

LLM中表格处理与多模态表格理解

文档处理中不可避免的遇到表格&#xff0c;关于表格的处理问题&#xff0c;整理如下&#xff0c;供各位参考。 问题描述 RAG中&#xff0c;对上传文档完成版式处理后进行切片&#xff0c;切片前如果识别文档元素是表格&#xff0c;那么则需要对表格进行处理。一般而言&#x…