.Net 腾讯云OCR对接
公司有自动识别身份证的需求,于是就采用了腾讯云的OCR自动识别功能,由于对接过程中踩坑较多,于是就将对接的过程及示例整理出来,方便有这方面需求的同志
腾讯云OCR识别官方文档:https://cloud.tencent.com/document/product/1007/35920
对接主要分为以下几个步骤
- 获取 Access Token
- 获取 SIGN ticket
- 生成签名
- OCR API 调用
一.获取 Access Token
注意事项
- 所有场景默认采用 UTF-8 编码。
- Access Token 必须缓存在磁盘,并定时刷新,且不能并发刷新,建议每20分钟请求新的 Access Token,获取之后立即使用最新的 Access Token。旧的只有一分钟的并存期。
- 如果未按照上述做定时刷新,可能导致鉴权不通过,影响人脸服务正常调用。
- 每次用户登录时必须重新获取 NONCE ticket。
private static string RedisAccessTokenKey { get; set; } = "TencentCloudAccessTokenKey-"; private static string RedisSignTicket { get; set; } = "TencentCloudSignTicket-"; private static string GetAccessToken() { string redisAccessKey = (RedisAccessTokenKey + Config.TencentCloudAppId); TencentCloudTokenInfoValidity validity = CacheManager.Instance.Get<TencentCloudTokenInfoValidity>(redisAccessKey); //如果为空保存更新 if (validity == null || (DateTime.Now - validity.LastUpdateTime).TotalSeconds > (int.Parse(validity.TokenInfo.ExpireIn) - 60)) { using (RedLockManager.GetTencentCloudAccessToken(Config.TencentCloudAppId)) { //拿到锁后,再次尝试 validity = CacheManager.Instance.Get<TencentCloudTokenInfoValidity>(redisAccessKey); if (validity == null || (DateTime.Now - validity.LastUpdateTime).TotalSeconds > (int.Parse(validity.TokenInfo.ExpireIn) - 60)) { string apiUrl = $"https://miniprogram-kyc.tencentcloudapi.com/api/oauth2/access_token?app_id={Config.TencentCloudAppId}&secret={Config.TencentCloudSecret}&grant_type=client_credential&version=1.0.0"; var tencentCloudApiResponse = TencentCloudApiRequestHelper.Get<GetTencentCloudTokenResponse>(apiUrl, new Operater { Name = "GetAccessToken" }); if (tencentCloudApiResponse == null) { throw new ArgumentNullException($"请求错误,url:{apiUrl},响应值为null"); } validity = new TencentCloudTokenInfoValidity() { LastUpdateTime = DateTime.Now, TokenInfo = new TencentCloudTokenData { AccessToken = tencentCloudApiResponse.AccessToken, ExpireIn = tencentCloudApiResponse.ExpireIn } }; //提前三分钟过期 CacheManager.Instance.Set(redisAccessKey, validity, ((int.Parse(tencentCloudApiResponse.ExpireIn) / 60)) - 3); } } } return validity.TokenInfo.AccessToken; }
二.获取 SIGN ticket
注意事项
- 前置条件:请合作方确保 Access Token 已经正常获取,获取方式请参见 获取 Access Token。
- 用途:SIGN ticket 是合作方后台服务端业务请求生成签名鉴权参数之一,用于后台查询验证结果、调用其他业务服务等。
- API ticket 的 SIGN 类型,必须缓存在磁盘,并定时刷新,刷新的机制如下:
- 因为 API ticket 依赖于 Access Token 为了简单方便,建议将 API ticket 与 Access Token 绑定,每20分钟定时刷新,且不能并发刷新。
- 获取新的之后请立即使用最新的,旧的有一分钟的并存期。
- 如果未按照上述做定时刷新,可能导致鉴权不通过,影响人脸服务正常调用。
public static string GetSignTicket() { string redisAccessKey = (RedisSignTicket + Config.TencentCloudAppId); TencentCloudTicketInfoValidity validity = CacheManager.Instance.Get<TencentCloudTicketInfoValidity>(redisAccessKey); //如果为空保存更新 if (validity == null || (DateTime.Now - validity.LastUpdateTime).TotalSeconds > (int.Parse(validity.TicketInfo.ExpireIn) - 60)) { using (RedLockManager.GetTencentCloudSignTicket(Config.TencentCloudAppId)) { //拿到锁后,再次尝试 validity = CacheManager.Instance.Get<TencentCloudTicketInfoValidity>(redisAccessKey); if (validity == null || (DateTime.Now - validity.LastUpdateTime).TotalSeconds > (int.Parse(validity.TicketInfo.ExpireIn) - 60)) { string AccessToken = GetAccessToken(); string apiUrl = $"https://miniprogram-kyc.tencentcloudapi.com/api/oauth2/api_ticket?app_id={Config.TencentCloudAppId}&access_token={AccessToken}&type=SIGN&version=1.0.0"; var tencentCloudApiResponse = TencentCloudApiRequestHelper.Get<GetTencentCloudTicketResponse>(apiUrl, new Operater { Name = "GetSignTicket" }); if (tencentCloudApiResponse == null || tencentCloudApiResponse.Code != "0" || !tencentCloudApiResponse.Tickets.Any()) { throw new ArgumentNullException($"请求错误 url:{apiUrl} "); } validity = new TencentCloudTicketInfoValidity() { LastUpdateTime = DateTime.Now, TicketInfo = new TencentCloudTicketData { Value = tencentCloudApiResponse.Tickets.FirstOrDefault().Value, ExpireIn = tencentCloudApiResponse.Tickets.FirstOrDefault().ExpireIn } }; //提前三分钟过期 CacheManager.Instance.Set(redisAccessKey, validity, ((int.Parse(tencentCloudApiResponse.Tickets.FirstOrDefault().ExpireIn) / 60) - 3)); } } } return validity.TicketInfo.Value; }
三.生成签名
基本步骤
- 生成一个32位的随机字符串(字母和数字) nonce(登录时也要用到),将 webankAppId、orderNo、version、连同 ticket、nonce 共5个参数的值进行字典序排序。
- 将排序后的所有参数字符串拼接成一个字符串进行 SHA1 编码。
- SHA1 编码后的40位字符串作为签名(sign)。
List<string> signList = new List<string> { req.Version, req.WebankAppId, ticket, req.Nonce, req.OrderNo }; signList.Sort(string.CompareOrdinal); //字典序排序 string signStr = string.Join("", signList.ToArray()); req.Sign = TencentCloudApiRequestHelper.SHA1(signStr);
四.OCR API 调用
public static GetIDCardOCRResponse GetIDCardOCR(GetIDCardOCRRequest req) { try { string apiUrl = $"https://miniprogram-kyc.tencentcloudapi.com/api/paas/idcardocrapp?orderNo=" + req.OrderNo; var ticket = TencentCloudCommon.GetSignTicket(); req.WebankAppId = Config.TencentCloudAppId; req.Version = "1.0.0"; List<string> signList = new List<string> { req.Version, req.WebankAppId, ticket, req.Nonce, req.OrderNo }; signList.Sort(string.CompareOrdinal); string signStr = string.Join("", signList.ToArray()); req.Sign = TencentCloudApiRequestHelper.SHA1(signStr); var getIDCardOCRRes = TencentCloudApiRequestHelper.Post<GetIDCardOCRResponse>(apiUrl, req); return getIDCardOCRRes; } catch (Exception ex) { LoggerHelper.Logger.Error($"请求错误,GetIDCardOCR Exception:{ex}", ex); return null; } }