springboot 腾讯地图接口验签 java

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换成自己的即可

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

halcon ocr识别字符

基本代码 create_text_model_reader (auto, Universal_0-9A-Z_Rej.occ, TextModel) * Optionally specify text properties set_text_model_param (TextModel, [min_char_height,polarity], [30, dark_on_light]) find_text (ImageResult, TextModel, TextResultID) * Return c…

postman断言及变量及参数化

1&#xff1a;postman断言 断言&#xff1a;判断接口是否执行成功的过程 针对接口请求完成之后&#xff0c;针对他的响应状态码及响应信息进行判断,代码如下&#xff1a; //判断响应信息状态码是否正确 pm.test("Status code is 200", function () { pm.response.…

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

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

Linux1(介绍与基本命令1)

目录 一、初始Linux 1. Linux的起源 2. Linux是什么&#xff1f; 3. Linux内核版本 4. Linux的应用 5. 终端 6. Shell 7. Linux目录结构 二、基本命令 1. 基本的命令格式 2. shutdown 关机命令 3. pwd 当前工作目录 4. ls 查看目录内容 5. cd 改变工作目录 …

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

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

ctfshow web 单身杯

web签到 <?phperror_reporting(0); highlight_file(__FILE__);$file $_POST[file];if(isset($file)){if(strrev($file)$file){ //翻转函数include $file;}}要进行反转并且包含文件用data协议 自己写不好写可以用函数帮你翻转 <?php $adata:text/plain,<?eval(…

HTML中的资源提示关键词

渲染阻塞问题 之前在学习浏览器的渲染原理的时候我们就知道&#xff1a;因为浏览器一次只能开启一个渲染主线程&#xff0c;所以当浏览器解析到script标签时会停止DOM树的构建&#xff0c;转而去执行script&#xff0c;如果script中引用的是外部脚本&#xff0c;则浏览器会先从…

你还不会选ProfiNET和EtherCAT网线?

在现代工业自动化领域&#xff0c;ProfiNET和EtherCAT是两种非常流行的通信协议。选择合适的网线对于确保通信的稳定性和效率至关重要。 ProfiNET是什么&#xff1f; ProfiNET是一种基于以太网的通信协议&#xff0c;由德国西门子公司开发。它支持实时通信&#xff0c;广泛应用…

玩转Matlab-Simscape(初级)- 10 - 基于COMSOLSimulink 凸轮机构的控制仿真

** 玩转Matlab-Simscape&#xff08;初级&#xff09;- 10 - 基于COMSOL&Simulink 凸轮机构的控制仿真 ** 目录 玩转Matlab-Simscape&#xff08;初级&#xff09;- 10 - 基于COMSOL&Simulink 凸轮机构的控制仿真 前言一、简介二、在Solidworks中创建3D模型&#xff…

05-5.4.1 树的存储结构

&#x1f44b; Hi, I’m Beast Cheng &#x1f440; I’m interested in photography, hiking, landscape… &#x1f331; I’m currently learning python, javascript, kotlin… &#x1f4eb; How to reach me --> 458290771qq.com 喜欢《数据结构》部分笔记的小伙伴可以…

商家转账到零钱怎么申请

开通商家转账到零钱功能涉及到多个步骤&#xff0c;包括资格审核、材料准备、提交申请等。以下是详细的步骤&#xff1a; 1. 确认开通条件&#xff1a; - 商家需要先成为微信支付商户。 - 商家的微信支付账户没有历史违规记录。 - 商家主体为企业资质。 - 商家系统已经上线并可…

Linux驱动面试题

1.导出符号表的原理&#xff1f; 2.字符设备驱动的框架流程 open read wirte close 是系统调用&#xff08;从用户空间进入内核空间的唯一的方法&#xff09;会产生swi软中断《也会存在软中断号》&#xff08;从User模式切换到SVC&#xff08;管理模式&#xff09;下因为在…

天锐绿盾加密软件,它的适用范围是什么?

天锐绿盾数据防泄密软件的适用范围广泛&#xff0c;主要可以归纳为以下几点&#xff1a; 行业适用性&#xff1a; 适用于各个行业&#xff0c;包括但不限于制造业、设计行业、软件开发、金融服务等&#xff0c;特别是对数据安全性要求较高的行业。企业规模与类型&#xff1a; 适…

C# Winform图形绘制

WinForms 应用程序中的控件是基于窗体的&#xff0c;当控件需要重绘时&#xff0c;它会向父窗体发送一个消息请求重绘。但是&#xff0c;控件本身并不直接处理绘制命令&#xff0c;所以你不能直接在控件上绘制图形。 解决方法&#xff1a; 重写控件的OnPaint方法使用CreateGr…

网站改成HTTPS方法

网站改成HTTPS只要网站没有特殊性的要求&#xff0c;绝大部分网站很轻松的就可以完成&#xff0c;尤其是CMS类似的网站系统或者自助搭建的网站&#xff08;比如&#xff1a;这种网站可以在网站后台一次性安装并且生效&#xff09;。 基本要求 将网站改成HTTPS有2个前提&#…

MMpose安装实例

摘要&#xff1a; 这个大数据训练发展较快&#xff0c;各种版本问题&#xff0c;不太好匹配&#xff0c;仅是安装就会大费周章。本文图文并茂的描述了一种成功的安装方式。仅供参考。 使用的win版本是win11&#xff0c;英伟达显卡是GeForce GTX 1660 SUPER。 1.cuda版本选择 通…

FFmpeg中内存分配和释放相关的源码:av_malloc函数、av_mallocz函数、av_free函数和av_freep函数分析

一、av_malloc函数分析 &#xff08;一&#xff09;av_malloc函数的声明 av_malloc函数的声明放在在FFmpeg源码&#xff08;本文演示用的FFmpeg源码版本为5.0.3&#xff0c;该ffmpeg在CentOS 7.5上通过10.2.1版本的gcc编译&#xff09;的头文件libavutil/mem.h中&#xff1a;…

cve_2014_3120-Elasticsearch-rce-vulfocus靶场

1.背景 来源&#xff1a;ElasticSearch&#xff08;CVE-2014-3120&#xff09;命令执行漏洞复现_mvel 漏洞-CSDN博客 参考&#xff1a;https://www.cnblogs.com/huangxiaosan/p/14398307.html 老版本ElasticSearch支持传入动态脚本&#xff08;MVEL&#xff09;来执行一些复…

万字长文详述 - 带你了解Jvm虚拟机运行时数据区

JVM虚拟机&#xff0c;对大部分Java程序员而言&#xff0c;是既熟悉又陌生的存在&#xff0c;Java程序在虚拟机的自动内存管理机制帮助下&#xff0c;减少了绝大部分的内存管理工作。但也正是因为如此&#xff0c;虚拟机如果出现了内存溢出或者泄露的情况&#xff0c;问题排查、…

基于YOLOv8m的船舶检测(附数据集和Coovally操作步骤)

本文主要内容:详细介绍了船舶检测整个过程&#xff0c;从创建数据集到训练模型再到预测结果全部可视化操作与分析。 文末有数据集获取方式&#xff0c;请先看检测效果 现状 船舶检测和识别是一项重要的任务&#xff0c;它涉及到航运安全、港口管理、海洋保护等方面&#xff0c…