Java集成腾讯云OCR身份证识别接口
一、背景
项目用到身份证识别获取人员信息的功能,于是想到了腾讯云提供这样的API。在整合代码过程都很顺利,利用腾讯云官方SDK很快集成进来。但是在上测试环境部署时有了新的问题,通过Nginx代理后的环境无法访问到目标腾讯云接口,遂有了如下的改造过程。
二、SDK集成Demo
首先是Maven依赖树:
<dependency><groupId>com.tencentcloudapi</groupId><artifactId>tencentcloud-sdk-java</artifactId><version>4.0.11</version></dependency>
然后是腾讯云提供的调试代码,改造了一部分:
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import com.tencentcloudapi.common.profile.ClientProfile;
import com.tencentcloudapi.common.profile.HttpProfile;
import com.tencentcloudapi.ocr.v20181119.OcrClient;
import com.tencentcloudapi.ocr.v20181119.models.IDCardOCRRequest;
import com.tencentcloudapi.ocr.v20181119.models.IDCardOCRResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;import java.text.ParseException;@Component
@Slf4j
@PropertySource("classpath:/properties/tencentCard.properties")
public class TencentUtil {@Value("${secretId}")private String secretId;@Value("${secretKey}")private String secretKey;@Value("${tencentUrl}")private String tencentUrl;public RecognitionView recognition(String cBase64) {if (cBase64.length() > 10485760) {throw new BusinessException("证件识别失败:图片文件太大");}RecognitionView tRecognitionView = new RecognitionView();try{// 实例化一个认证对象,入参需要传入腾讯云账户 SecretId 和 SecretKey,此处还需注意密钥对的保密// 代码泄露可能会导致 SecretId 和 SecretKey 泄露,并威胁账号下所有资源的安全性。以下代码示例仅供参考,建议采用更安全的方式来使用密钥,请参见:https://cloud.tencent.com/document/product/1278/85305// 密钥可前往官网控制台 https://console.cloud.tencent.com/cam/capi 进行获取Credential cred = new Credential(secretId, secretKey);// 实例化一个http选项,可选的,没有特殊需求可以跳过HttpProfile httpProfile = new HttpProfile();httpProfile.setEndpoint(tencentUrl);// 实例化一个client选项,可选的,没有特殊需求可以跳过ClientProfile clientProfile = new ClientProfile();clientProfile.setHttpProfile(httpProfile);// 实例化要请求产品的client对象,clientProfile是可选的OcrClient client = new OcrClient(cred, "ap-beijing", clientProfile);// 实例化一个请求对象,每个接口都会对应一个request对象IDCardOCRRequest req = new IDCardOCRRequest();req.setImageBase64(cBase64);// 返回的resp是一个IDCardOCRResponse的实例,与请求对象对应IDCardOCRResponse resp = client.IDCardOCR(req);tRecognitionView.setRecognitionView(resp);// 输出json格式的字符串回包log.info("证件识别返回参数:" + IDCardOCRResponse.toJsonString(resp));} catch (Exception e) {String tError = "证件识别失败:" + e.getMessage();log.error(tError);throw new BusinessException(tError);}return tRecognitionView;}}
postman调试后可以正常获取到解析内容

三、Nginx调用失败及解决
部署到测试环境后,由于内网服务器需要代理到外网服务器进行外网地址的访问,此时便提示证书找不到的错误。

找问题的过程很坎坷,从证书的有效性、代理的连通性、SDK的限制性等等,研究了将近三天,就连做梦都在思考哪里有问题。最后实在没了方向,决定从根上入手,跳过证书验证。
跳过验证分为两步,1、放弃SDK请求方式,需要手写Connection;2、增加跳过证书的代码逻辑。于是便有了如下代码:
import com.alibaba.fastjson.JSON;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.JsonResponseModel;
import com.tencentcloudapi.common.profile.ClientProfile;
import com.tencentcloudapi.common.profile.HttpProfile;
import com.tencentcloudapi.ocr.v20181119.OcrClient;
import com.tencentcloudapi.ocr.v20181119.models.IDCardOCRRequest;
import com.tencentcloudapi.ocr.v20181119.models.IDCardOCRResponse;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.apache.commons.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.*;@Component
@Slf4j
@PropertySource("classpath:/properties/tencentCard.properties")
public class TencentUtil {@Value("${secretId}")private String secretId;@Value("${secretKey}")private String secretKey;@Value("${tencentUrl}")private String tencentUrl;private final static String CT_JSON = "application/json; charset=utf-8"; // 请求头内容类型private final static Charset UTF8 = StandardCharsets.UTF_8; // 编码格式static final OkHttpClient HTTP_CLIENT = new BaiduUtil().getUnsafeOkHttpClient();public RecognitionView recognitionPassSSL(byte[] cbyte) {String ImageBase64 = Base64.encodeBase64String(cbyte);Map<String, Object> tRequest = new HashMap<>();tRequest.put("ImageBase64", ImageBase64);String requestData = JSON.toJSONString(tRequest);RecognitionView tRecognitionView = new RecognitionView();try {MediaType mediaType = MediaType.parse(CT_JSON);RequestBody body = RequestBody.create(mediaType, requestData);Request.Builder tBuilder = new Request.Builder().url("https://" + tencentUrl).method("POST", body);this.assembleHeader(tBuilder,this.sign(requestData));Request request = tBuilder.build();Response response = HTTP_CLIENT.newCall(request).execute();String tResult = response.body().string();Gson gson = new Gson();log.info("证件识别返回参数:" + tResult);if (tResult.contains("Error")) {//{"Response":{"RequestId":"4dd26ba4-3e0e-412b-b5a3-047829d5541f","Error":{"Code":"FailedOperation.ImageNoIdCard","Message":"照片未检测到身份证"}}}JsonResponseModel resp = JSON.parseObject(tResult,JsonResponseModel.class);TencentErrorView tTencentErrorView = JSON.parseObject(JSON.toJSONString(resp.response),TencentErrorView.class);throw new BusinessException(tTencentErrorView.getError().getMessage());} else {//{"name": "吕能仕","id": "362323194911046513","nation": "汉","sex": "男","birthDay": "1949-11-04","address": "江西省上饶市玉山县四股桥乡丁村村喻村4号","age_unit": "岁","age_value": "73"}Type type = new TypeToken<JsonResponseModel<IDCardOCRResponse>>() {}.getType();JsonResponseModel<IDCardOCRResponse> resp = gson.fromJson(tResult, type);tRecognitionView.setRecognitionView(resp.response);}} catch (Exception e) {String tError = "证件识别失败:" + e.getMessage();log.error(tError);throw new BusinessException(tError);}return tRecognitionView;}private void assembleHeader(Request.Builder tBuilder, Map<String, String> sign) {Set<String> strings = sign.keySet();for (String tName : strings) {tBuilder.addHeader(tName, sign.get(tName));}}public RecognitionView recognition(byte[] cbyte) {String tBase64 = Base64.encodeBase64String(cbyte);RecognitionView tRecognitionView = new RecognitionView();try {// 实例化一个认证对象,入参需要传入腾讯云账户 SecretId 和 SecretKey,此处还需注意密钥对的保密// 代码泄露可能会导致 SecretId 和 SecretKey 泄露,并威胁账号下所有资源的安全性。以下代码示例仅供参考,建议采用更安全的方式来使用密钥,请参见:https://cloud.tencent.com/document/product/1278/85305// 密钥可前往官网控制台 https://console.cloud.tencent.com/cam/capi 进行获取Credential cred = new Credential(secretId, secretKey);// 实例化一个http选项,可选的,没有特殊需求可以跳过HttpProfile httpProfile = new HttpProfile();httpProfile.setEndpoint(tencentUrl);// 实例化一个client选项,可选的,没有特殊需求可以跳过ClientProfile clientProfile = new ClientProfile();clientProfile.setHttpProfile(httpProfile);// 实例化要请求产品的client对象,clientProfile是可选的OcrClient client = new OcrClient(cred, "ap-beijing", clientProfile);// 实例化一个请求对象,每个接口都会对应一个request对象IDCardOCRRequest req = new IDCardOCRRequest();req.setImageBase64(tBase64);// 返回的resp是一个IDCardOCRResponse的实例,与请求对象对应IDCardOCRResponse resp = client.IDCardOCR(req);tRecognitionView.setRecognitionView(resp);// 输出json格式的字符串回包log.info("证件识别返回参数:" + IDCardOCRResponse.toJsonString(resp));} catch (Exception e) {String tError = "证件识别失败:" + e.getMessage();log.error(tError);throw new BusinessException(tError);}return tRecognitionView;}/*** API签名方法* @param data 发送的json串数据* @return 请求头map* @throws Exception 异常*/@SuppressWarnings({"JsonStandardCompliance", "DuplicatedCode"})private Map<String,String> sign(String data) throws Exception {String service = "ocr"; // 腾讯云服务器String host = "ocr.tencentcloudapi.com"; // 服务器地址String region = "ap-beijing"; // 服务器区域String action = "IDCardOCR"; // api接口名称String version = "2018-11-19"; // 接口版本号String algorithm = "TC3-HMAC-SHA256";// String timestamp = "1551113065";String timestamp = String.valueOf(System.currentTimeMillis() / 1000); // 时间戳SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");// 注意时区,否则容易出错sdf.setTimeZone(TimeZone.getTimeZone("UTC"));String date = sdf.format(new Date(Long.valueOf(timestamp + "000")));// ************* 步骤 1:拼接规范请求串 *************String httpRequestMethod = "POST"; // 请求方法String canonicalUri = "/";String canonicalQueryString = "";String canonicalHeaders = "content-type:application/json; charset=utf-8\n" + "host:" + host + "\n"; // 请求头信息String signedHeaders = "content-type;host"; // 签名头包含内容String payload = data; // 请求内容String hashedRequestPayload = sha256Hex(payload);String canonicalRequest = httpRequestMethod + "\n" + canonicalUri + "\n" + canonicalQueryString + "\n"+ canonicalHeaders + "\n" + signedHeaders + "\n" + hashedRequestPayload;// ************* 步骤 2:拼接待签名字符串 *************String credentialScope = date + "/" + service + "/" + "tc3_request";String hashedCanonicalRequest = sha256Hex(canonicalRequest);String stringToSign = algorithm + "\n" + timestamp + "\n" + credentialScope + "\n" + hashedCanonicalRequest;// ************* 步骤 3:计算签名 *************byte[] secretDate = hmac256(("TC3" + secretKey).getBytes(UTF8), date);byte[] secretService = hmac256(secretDate, service);byte[] secretSigning = hmac256(secretService, "tc3_request");String signature = DatatypeConverter.printHexBinary(hmac256(secretSigning, stringToSign)).toLowerCase();// ************* 步骤 4:拼接 Authorization *************String authorization = algorithm + " " + "Credential=" + secretId + "/" + credentialScope + ", "+ "SignedHeaders=" + signedHeaders + ", " + "Signature=" + signature;TreeMap<String, String> headers = new TreeMap<String, String>();headers.put("Authorization", authorization);headers.put("Content-Type", CT_JSON);headers.put("Host", host);headers.put("X-TC-Action", action);headers.put("X-TC-Timestamp", timestamp);headers.put("X-TC-Version", version);headers.put("X-TC-Region", region);return headers;}private static byte[] hmac256(byte[] key, String msg) throws Exception {Mac mac = Mac.getInstance("HmacSHA256");SecretKeySpec secretKeySpec = new SecretKeySpec(key, mac.getAlgorithm());mac.init(secretKeySpec);return mac.doFinal(msg.getBytes(UTF8));}private static String sha256Hex(String s) throws Exception {MessageDigest md = MessageDigest.getInstance("SHA-256");byte[] d = md.digest(s.getBytes(UTF8));return DatatypeConverter.printHexBinary(d).toLowerCase();}
}
四、总结
经过验证,该方式可以访问经过Nginx代理的腾讯云接口。整个解决过程缺少对问题现状的分析,并没有制定切入点,而是想到哪里改哪里,所以修改的过程异常煎熬。
后续对于问题的挖掘及解决要整体分析然后列出各个怀疑的情况和解决方案,然后对照着清单逐一排查,如此条理清晰的处理过程才会更有效的解决问题。
相关文章:
Java集成腾讯云OCR身份证识别接口
一、背景 项目用到身份证识别获取人员信息的功能,于是想到了腾讯云提供这样的API。在整合代码过程都很顺利,利用腾讯云官方SDK很快集成进来。但是在上测试环境部署时有了新的问题,通过Nginx代理后的环境无法访问到目标腾讯云接口,…...
C++之C++11引入enum class与传统enum关键字总结(二百五十一)
简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 人生格言: 人生…...
如何将word格式的文档转换成markdown格式的文档
如何将word格式的文档转换成markdown格式的文档 前言 A. 介绍Markdown和Word格式文档 什么是Markdown? Markdown是一种轻量级标记语言,旨在简化文本格式化和排版的过程。它以纯文本形式编写,通过使用简单的标记语法,使文档更具…...
Leetcode—2558.从数量最多的堆取走礼物【简单】
2023每日刷题(十二) Leetcode—2558.从数量最多的堆取走礼物 大顶堆实现代码 void swap(int *a, int *b) {int tmp *a;*a *b;*b tmp; }void downAdjustHeap(int *heap, int low, int high) {int i low;int j 2 * i 1;while(j < high) {if(j …...
【如何写论文】硕博学位论文的结构框架、过程与大纲分析
硕士论文可以说是毕业前最重要的一部分,也可以说是展示和检验你3年研究生学习的成果的一个考试。硕士论文答辩和检验合格,才能够顺利拿到毕业生和学位证,可见其重要性。 目录 一、基础框架1.1、摘要(Abstract)1.2、绪论…...
砷化镓(GaAs)纳米线 砷化镓纳米线 GaAs纳米线 瑞禧
砷化镓(GaAs)纳米线 名称:砷化镓(GaAs)纳米线 直径:50-400 nm 长度:10-80μm 纳米线是一种新型的材料结构,具有较小的直径和高的长度比,因此在纳米电子学、光电器件等领域有着广泛的应用前景…...
PostGreSQL:JSON|JSONB数据类型
JSON JSON 指的是 JavaScript 对象表示法(JavaScript Object Notation)JSON 是轻量级的文本数据交换格式JSON 独立于语言:JSON 使用 Javascript语法来描述数据对象,但是 JSON 仍然独立于语言和平台。JSON 解析器和 JSON 库支持许…...
树----数据结构
树的概念 树是一种非线性的数据结构,它是由 n (n>1) 个有限结点组成一个具有层次关系的集合,它看起来就像一颗倒挂的树,根朝上,叶朝下。由 0 个节点构成的树,叫做空树。 树的特点:每个结点有 0 个或多…...
GitLab定时备份
GitLab定时备份 文章目录 GitLab定时备份GitLab基础环境备份命令自动清理备份上传命令设置定时任务参考链接 GitLab基础环境 部署方式:Docker 版本:16.2.2 备份命令 Notes: 编写sh脚本时,不要使用Windows上的Notepad类似编辑…...
SQL IN 运算符
SQL IN 运算符 IN 运算符允许您在 WHERE 子句中指定多个值。 IN 运算符是多个 OR 条件的简写。 SQL IN 语法 SELECT column_name(s) FROM table_name WHERE column_name IN (value1, value2, ...); 或者 SELECT column_name(s) FROM table_name WHERE column_name IN (SELE…...
虚拟机构建单体项目及前后端分离项目
引言 在现代化办公环境中,会议是组织沟通、决策和合作的重要方式之一。为了提高会议的效率和质量,许多企业选择部署会议OA系统来实现会议管理的自动化和数字化。本博客将介绍如何部署和优化会议OA系统,并探讨前后端分离的SPA项目在此过程中的…...
代码浅析DLIO(一)---整体框架梳理
0. 简介 我们刚刚了解过DLIO的整个流程,我们发现相比于Point-LIO而言,这个方法更适合我们去学习理解,同时官方给出的结果来看DLIO的结果明显好于现在的主流方法,当然指的一提的是,这个DLIO是必须需要六轴IMU的&#x…...
Springboot的Container Images,docker加springboot
Spring Boot应用程序可以使用Dockerfiles容器化,或者使用Cloud Native Buildpacks来创建优化的docker兼容的容器映像,您可以在任何地方运行。 1. Efficient Container Images 很容易将Spring Boot fat jar打包为docker映像。然而,像在docke…...
c 从avi 视频中提取图片
avi 视频的视频流编码必须是jpeg,或者mjpg 直接用摄像头录取的视频都是这两种格式,不能用ffmpeg转成avi的视频。 #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdlib.…...
Jtti:Apache服务的反向代理及负载均衡怎么配置
配置Apache服务的反向代理和负载均衡可以帮助您分散负载并提高应用程序的可用性和性能。下面是一些通用的步骤,以配置Apache反向代理和负载均衡。 1. 安装和配置Apache: 确保您已经安装了Apache HTTP服务器。通常,Apache的配置文件位于/etc…...
82.二分查找
目录 什么是二分查找 一、左闭右闭写法[left,right] 代码演示: 二、左闭右开写法[left,right] 代码演示: 今天进行了二分查找的学习。 什么是二分查找 二分查找(Binary Search)是一种常用的搜索算法,也被称为折…...
线程是如何创建的
线程不是一个完全由内核实现的机制,它是由内核态和用户态合作完成的。pthread_create 不是一个系统调用,是 Glibc 库的一个函数,所以我们还要去 Glibc 里面去找线索。 首先处理的是线程的属性参数。例如前面写程序的时候,我们设置…...
owl_vit安装步骤
owl项目的clip目录与openai的clip重名了,import时容易找不到文件simple_tokenizer。 from clip import simple_tokenizer解决办法: 把clip项目下的simple_tokenizer.py拷贝到owl项目下的clip文件夹 cp simple_tokenizer.py /{project_dir}/scenic/scenic/projects…...
运行real.exe时出现NUM_METGRID_SOIL_LEVELS=0
本人在运行real.exe时,发现出现这样的报错: d01 2020-01-01_00:00:00 ---- ERROR: Mismatch between namelist and global attribute NUM_METGRID_SOIL_LEVELS NOTE: 2 namelist vs input data inconsistencies found. -------------- FATAL CALL…...
【数值计算方法】Gauss消元法及其Python/C实现
文章目录 一、基础理论1. 线性方程组2. Gauss消元法的详细步骤3. 注意事项 二、具体计算过程1. 用Gauss 消元法求A的LU分解,并由此求解方程组 Ax ba. 将A进行LU分解。b. 使用LU分解求解方程组Axb 三、代码实现1. Python代码实现2. C语言代码实现 Gauss消元法&#x…...
视觉slam十四讲实践部分记录——ch2、ch3
ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...
【JVM面试篇】高频八股汇总——类加载和类加载器
目录 1. 讲一下类加载过程? 2. Java创建对象的过程? 3. 对象的生命周期? 4. 类加载器有哪些? 5. 双亲委派模型的作用(好处)? 6. 讲一下类的加载和双亲委派原则? 7. 双亲委派模…...
【JVM】Java虚拟机(二)——垃圾回收
目录 一、如何判断对象可以回收 (一)引用计数法 (二)可达性分析算法 二、垃圾回收算法 (一)标记清除 (二)标记整理 (三)复制 (四ÿ…...
Web中间件--tomcat学习
Web中间件–tomcat Java虚拟机详解 什么是JAVA虚拟机 Java虚拟机是一个抽象的计算机,它可以执行Java字节码。Java虚拟机是Java平台的一部分,Java平台由Java语言、Java API和Java虚拟机组成。Java虚拟机的主要作用是将Java字节码转换为机器代码&#x…...
解析奥地利 XARION激光超声检测系统:无膜光学麦克风 + 无耦合剂的技术协同优势及多元应用
在工业制造领域,无损检测(NDT)的精度与效率直接影响产品质量与生产安全。奥地利 XARION开发的激光超声精密检测系统,以非接触式光学麦克风技术为核心,打破传统检测瓶颈,为半导体、航空航天、汽车制造等行业提供了高灵敏…...
Vue ③-生命周期 || 脚手架
生命周期 思考:什么时候可以发送初始化渲染请求?(越早越好) 什么时候可以开始操作dom?(至少dom得渲染出来) Vue生命周期: 一个Vue实例从 创建 到 销毁 的整个过程。 生命周期四个…...
redis和redission的区别
Redis 和 Redisson 是两个密切相关但又本质不同的技术,它们扮演着完全不同的角色: Redis: 内存数据库/数据结构存储 本质: 它是一个开源的、高性能的、基于内存的 键值存储数据库。它也可以将数据持久化到磁盘。 核心功能: 提供丰…...
raid存储技术
1. 存储技术概念 数据存储架构是对数据存储方式、存储设备及相关组件的组织和规划,涵盖存储系统的布局、数据存储策略等,它明确数据如何存储、管理与访问,为数据的安全、高效使用提供支撑。 由计算机中一组存储设备、控制部件和管理信息调度的…...
英国云服务器上安装宝塔面板(BT Panel)
在英国云服务器上安装宝塔面板(BT Panel) 是完全可行的,尤其适合需要远程管理Linux服务器、快速部署网站、数据库、FTP、SSL证书等服务的用户。宝塔面板以其可视化操作界面和强大的功能广受国内用户欢迎,虽然官方主要面向中国大陆…...
aurora与pcie的数据高速传输
设备:zynq7100; 开发环境:window; vivado版本:2021.1; 引言 之前在前面两章已经介绍了aurora读写DDR,xdma读写ddr实验。这次我们做一个大工程,pc通过pcie传输给fpga,fpga再通过aur…...
