本次将分享给大家,调用腾讯云API实现人脸识别功能(参考API https://cloud.tencent.com/document/api/867/32777)
package com.sf.vsolution.hb.sfce.util.wechat.face; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.sf.vsolution.hb.sfce.util.wechat.face.req.CreateFace; import com.sf.vsolution.hb.sfce.util.wechat.face.req.CreateGroup; import com.sf.vsolution.hb.sfce.util.wechat.face.req.CreatePerson; import com.sf.vsolution.hb.sfce.util.wechat.face.req.GetPersonBaseInfo; import com.sf.vsolution.hb.sfce.util.wechat.face.resp.GetPersonBaseInfoResp; import com.sf.vsolution.hb.sfce.util.wechat.face.resp.VerifyFaceResp; import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParams; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import java.io.UnsupportedEncodingException; import java.util.*; /** * @description: 人脸识别工具类 * @author: zhucj * @date: 2019-10-18 16:10 */ @Slf4j @Component public class AFSUtils { @Value("${tencent.secretId}") private String sercretId; @Value("${tencent.secretKey}") private String sercretKey; /** * 创建人员库 * @param createGroup * @return */ @ApiOperation(value = "创建人员库") @ApiImplicitParams({ @ApiImplicitParam(name = "groupName",value = "人员库名称",required = true,dataType = "String"), @ApiImplicitParam(name = "groupId",value = "人员库ID",required = true,dataType = "String") }) public R createGroup(CreateGroup createGroup){ //构建签名参数 TreeMap treeMap = createPublicMap("CreateGroup", "2018-03-01"); treeMap.put("GroupName",createGroup.getGroupName()); treeMap.put("GroupId",createGroup.getGroupId()); if(!StringUtils.isEmpty(createGroup.getGroupExDescriptions01())){ treeMap.put("GroupExDescriptions.0",createGroup.getGroupId()); } if(!StringUtils.isEmpty(createGroup.getGroupExDescriptions02())){ treeMap.put("GroupExDescriptions.1",createGroup.getGroupId()); } if(!StringUtils.isEmpty(createGroup.getGroupExDescriptions03())){ treeMap.put("GroupExDescriptions.2",createGroup.getGroupId()); } if(!StringUtils.isEmpty(createGroup.getTag())){ treeMap.put("Tag",createGroup.getTag()); } try { treeMap.put("Signature",SignUtils.sign(treeMap,HttpMethodEnum.GET,SignMenodEnum.HMACSHA1, JSON.toJSONString(createGroup), AFSServerApi.SERVER_NAME_API,sercretKey,ContentTypeEnum.JSON)); } catch (Exception e) { log.error("签名异常:{}",e.getMessage()); R.error("签名异常").setCode(SystemConstants.SERVER_ERROR_CODE); } String reqUrl = null; try { reqUrl = SignUtils.getUrl(treeMap, AFSServerApi.SERVER_NAME_API); } catch (UnsupportedEncodingException e) { log.error("URL编码异常:{}",e.getMessage()); return R.error("URL编码异常").setCode(SystemConstants.SERVER_ERROR_CODE); } try { String s = HttpUtil.httpGet(reqUrl); JSONObject jsonObject = JSON.parseObject(s); String response = jsonObject.getString("Response"); JSONObject error =(JSONObject) JSON.parseObject(response).get("Error"); if (Objects.nonNull(error)){ return R.error(String.valueOf(error.get("Message"))).setCode(SystemConstants.SERVER_ERROR_CODE); }else { return R.ok(null,"创建成功"); } } catch (Exception e) { log.error("创建失败:{}",e.getMessage()); return R.error("创建失败").setCode(SystemConstants.SERVER_ERROR_CODE); } } /** * 创建人员 * @param createPerson * @return */ @ApiOperation(value = "创建人员") @ApiImplicitParams({ @ApiImplicitParam(name = "groupId",value = "人员库ID",required = true,dataType = "String"), @ApiImplicitParam(name = "personName",value = "人员名字",required = true,dataType = "String"), @ApiImplicitParam(name = "personId",value = "人员ID",required = true,dataType = "String"), @ApiImplicitParam(name = "gender",value = "性别",required = true,dataType = "Integer"), @ApiImplicitParam(name = "image",value = "图片名字Base64字符串",required = true,dataType = "String") }) public R createPerson(CreatePerson createPerson){ //构建签名参数map TreeMap treeMap = createPublicMap("CreatePerson", "2018-03-01"); //构建JSON格式参数 HashMap<String,Object> hashMap = new HashMap<>(); hashMap.put("PersonName",createPerson.getPersonName()); hashMap.put("PersonId",createPerson.getPersonId()); hashMap.put("GroupId",createPerson.getGroupId()); hashMap.put("Image",createPerson.getImage()); hashMap.put("Gender",createPerson.getGender()==null?0:createPerson.getGender()); if (!StringUtils.isEmpty(createPerson.getPersonExDescription01())){ hashMap.put("PersonExDescriptionInfos.0.PersonExDescriptionIndex",0); hashMap.put("PersonExDescriptionInfos.0.PersonExDescription",createPerson.getPersonExDescription01()); } if (!StringUtils.isEmpty(createPerson.getPersonExDescription02())){ hashMap.put("PersonExDescriptionInfos.1.PersonExDescriptionIndex",1); hashMap.put("PersonExDescriptionInfos.1.PersonExDescription",createPerson.getPersonExDescription02()); } if (!StringUtils.isEmpty(createPerson.getPersonExDescription03())){ hashMap.put("PersonExDescriptionInfos.2.PersonExDescriptionIndex",2); hashMap.put("PersonExDescriptionInfos.2.PersonExDescription",createPerson.getPersonExDescription03()); } String sign = null; try { sign = SignUtils.sign(treeMap, HttpMethodEnum.POST, SignMenodEnum.TC3_HMAC_SHA256, JSON.toJSONString(hashMap) , AFSServerApi.SERVER_NAME_API, sercretKey, ContentTypeEnum.JSON); } catch (Exception e) { log.error("签名异常:{}",e.getMessage()); return R.error("签名异常"); } try { String respJson = HttpUtil.httpPost(AFSServerApi.SERVER_NAME_API, JSON.parseObject(sign,Map.class),hashMap); JSONObject jsonObject = JSON.parseObject(respJson); String response = jsonObject.getString("Response"); JSONObject error =(JSONObject) JSON.parseObject(response).get("Error"); if (Objects.nonNull(error)){ return R.error(String.valueOf(error.get("Message"))).setCode(SystemConstants.SERVER_ERROR_CODE); }else { return R.ok(null,"创建成功"); } } catch (Exception e) { log.error("创建失败:{}",e.getMessage()); return R.error("创建失败"); } } /** * 查询人员列表 * @param getPersonBaseInfo * @return */ @ApiOperation(value = "查询人员列表") @ApiImplicitParams({ @ApiImplicitParam(name = "personId",value = "人员ID",required = true,dataType = "String"), }) public R getPersonBaseInfo(GetPersonBaseInfo getPersonBaseInfo){ //封装公共map签名 TreeMap treeMap = createPublicMap("GetPersonBaseInfo", "2018-03-01"); //HashMap<String,Object> hashMap = new HashMap<>(); //封装请求参数 treeMap.put("PersonId",getPersonBaseInfo.getPersonId()); String sign = null; try { sign = SignUtils.sign(treeMap, HttpMethodEnum.GET, SignMenodEnum.HMACSHA1, null, AFSServerApi.SERVER_NAME_API, sercretKey, ContentTypeEnum.JSON); } catch (Exception e) { log.error("签名异常",e.getMessage()); return R.error("签名异常"); } treeMap.put("Signature",sign); try { String url = SignUtils.getUrl(treeMap, AFSServerApi.SERVER_NAME_API); String respJson = HttpUtil.httpGet(url); JSONObject jsonObject = JSON.parseObject(respJson); String response = jsonObject.getString("Response"); JSONObject error =(JSONObject) JSON.parseObject(response).get("Error"); if (Objects.nonNull(error)){ return R.error(String.valueOf(error.get("Message"))).setCode(SystemConstants.SERVER_ERROR_CODE); }else { return R.ok(JSON.parseObject(response, GetPersonBaseInfoResp.class)); } } catch (Exception e) { log.error("查询失败:{}",e.getMessage()); return R.error("查询失败:"+e.getMessage()); } }; /** * 创建人脸 * @param createFace * @return */ public R createFace(CreateFace createFace){ //封装公共请求参数 TreeMap treeMap = createPublicMap("CreateFace", "2018-03-01"); //封装请求参数 HashMap<String,Object> hashMap = new HashMap<>(); hashMap.put("PersonId",createFace.getPersonId()); ArrayList<String> images = new ArrayList<>(); if (!StringUtils.isEmpty(createFace.getImg01())){ images.add(createFace.getImg01()); } if (!StringUtils.isEmpty(createFace.getImg02())){ images.add(createFace.getImg02()); } if (!StringUtils.isEmpty(createFace.getImg03())){ images.add(createFace.getImg03()); } if (!StringUtils.isEmpty(createFace.getImg04())){ images.add(createFace.getImg04()); } if (!StringUtils.isEmpty(createFace.getImg05())){ images.add(createFace.getImg05()); } hashMap.put("Images",images); //获取请求头JSON数据 String haerdsJson = null; try { haerdsJson = SignUtils.sign(treeMap, HttpMethodEnum.POST, SignMenodEnum.TC3_HMAC_SHA256, JSON.toJSONString(hashMap), AFSServerApi.SERVER_NAME_API, sercretKey, ContentTypeEnum.JSON); } catch (Exception e) { e.printStackTrace(); } //发起post请求 try { String respJson = HttpUtil.httpPost(AFSServerApi.SERVER_NAME_API, JSON.parseObject(haerdsJson, TreeMap.class), hashMap); JSONObject jsonObject = JSON.parseObject(respJson); String response = jsonObject.getString("Response"); JSONObject error =(JSONObject) JSON.parseObject(response).get("Error"); if (Objects.nonNull(error)){ return R.error(String.valueOf(error.get("Message"))).setCode(SystemConstants.SERVER_ERROR_CODE); }else { return R.ok("新增成功"); } } catch (Exception e) { log.error("新增失败:{}",e.getMessage()); return R.error("新增失败:"+e.getMessage()); } } public R verifyFace(CreatePerson createPerson){ //封装公共请求参数 TreeMap treeMap = createPublicMap("VerifyFace", "2018-03-01"); //封装请求参数 HashMap<String,Object> hashMap = new HashMap<>(); hashMap.put("PersonId",createPerson.getPersonId()); hashMap.put("Image",createPerson.getImage()); //获取请求头JSON数据 String haerdsJson = null; try { haerdsJson = SignUtils.sign(treeMap, HttpMethodEnum.POST, SignMenodEnum.TC3_HMAC_SHA256, JSON.toJSONString(hashMap), AFSServerApi.SERVER_NAME_API, sercretKey, ContentTypeEnum.JSON); } catch (Exception e) { e.printStackTrace(); return R.error("签名异常"); } //发起post请求 try { String respJson = HttpUtil.httpPost(AFSServerApi.SERVER_NAME_API, JSON.parseObject(haerdsJson, TreeMap.class), hashMap); JSONObject jsonObject = JSON.parseObject(respJson); String response = jsonObject.getString("Response"); JSONObject error =(JSONObject) JSON.parseObject(response).get("Error"); if (Objects.nonNull(error)){ return R.error(String.valueOf(error.get("Message"))).setCode(SystemConstants.SERVER_ERROR_CODE); }else { VerifyFaceResp verifyFaceResp = JSON.parseObject(response, VerifyFaceResp.class); if (verifyFaceResp.getIsMatch()){ return R.ok(null,"识别成功,相识度"+verifyFaceResp.getScore()+"%"); }else { return R.ok(null,"识别不成功,相识度"+verifyFaceResp.getScore()+"%"); } } } catch (Exception e) { log.error("识别失败:{}",e.getMessage()); return R.error("识别失败:"+e.getMessage()); } } /** * 获取当前时间戳,单位秒 * @return */ public static long getCurrentTimestamp() { return System.currentTimeMillis()/1000; } /** * 封装请求参数 * @param action * @param version * @return */ public TreeMap createPublicMap(String action,String version){ TreeMap<String,Object> treeMap = new TreeMap<>(); treeMap.put("Action",action); treeMap.put("Version",version); treeMap.put("Timestamp",getCurrentTimestamp()); treeMap.put("Nonce",new Random().nextInt(Integer.MAX_VALUE)); treeMap.put("SecretId",sercretId); return treeMap; } }
package com.sf.vsolution.hb.sfce.util.wechat.face; import com.alibaba.fastjson.JSON; import lombok.extern.slf4j.Slf4j; import org.apache.http.HttpEntity; import org.apache.http.NameValuePair; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpRequestBase; import org.apache.http.client.utils.URIBuilder; import org.apache.http.config.Registry; import org.apache.http.config.RegistryBuilder; import org.apache.http.conn.socket.ConnectionSocketFactory; import org.apache.http.conn.socket.PlainConnectionSocketFactory; import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.message.BasicNameValuePair; import org.apache.http.ssl.SSLContextBuilder; import org.apache.http.ssl.SSLContexts; import org.apache.http.ssl.TrustStrategy; import org.apache.http.util.EntityUtils; import javax.net.ssl.SSLContext; import java.io.File; import java.io.FileInputStream; import java.nio.charset.StandardCharsets; import java.security.KeyStore; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.List; import java.util.Map; @Slf4j public class HttpUtil { public static final ContentType TEXT_PLAIN = ContentType.create("text/plain", StandardCharsets.UTF_8); /** * HttpClient 连接池 */ private static PoolingHttpClientConnectionManager cm = null; static { // 初始化连接池,可用于请求HTTP/HTTPS(信任所有证书) cm = new PoolingHttpClientConnectionManager(getRegistry()); // 整个连接池最大连接数 cm.setMaxTotal(200); // 每路由最大连接数,默认值是2 cm.setDefaultMaxPerRoute(5); } /** * 发送 HTTP GET请求 * <p>不带请求参数和请求头</p> * @param url 地址 * @return * @throws Exception */ public static String httpGet(String url) throws Exception { log.info("请求参数:{}",url); HttpGet httpGet = new HttpGet(url); return doHttp(httpGet); } /** * 发送 HTTP GET请求 * <p>带请求参数,不带请求头</p> * @param url 地址 * @param params 参数 * @return * @throws Exception * @throws Exception */ public static String httpGet(String url, Map<String, Object> params) throws Exception { // 转换请求参数 List<NameValuePair> pairs = covertParams2NVPS(params); // 装载请求地址和参数 URIBuilder ub = new URIBuilder(); ub.setPath(url); ub.setParameters(pairs); HttpGet httpGet = new HttpGet(ub.build()); return doHttp(httpGet); } /** * 发送 HTTP GET请求 * <p>带请求参数和请求头</p> * @param url 地址 * @param headers 请求头 * @param params 参数 * @return * @throws Exception * @throws Exception */ public static String httpGet(String url, Map<String, Object> headers, Map<String, Object> params) throws Exception { // 转换请求参数 List<NameValuePair> pairs = covertParams2NVPS(params); // 装载请求地址和参数 URIBuilder ub = new URIBuilder(); ub.setPath(url); ub.setParameters(pairs); HttpGet httpGet = new HttpGet(ub.build()); // 设置请求头 for (Map.Entry<String, Object> param : headers.entrySet()){ httpGet.addHeader(param.getKey(), String.valueOf(param.getValue()));} return doHttp(httpGet); } /** * 发送 HTTP POST请求 * <p>不带请求参数和请求头</p> * * @param url 地址 * @return * @throws Exception */ public static String httpPost(String url) throws Exception { HttpPost httpPost = new HttpPost(url); return doHttp(httpPost); } /** * 发送 HTTP POST请求 * <p>带请求参数,不带请求头</p> * * @param url 地址 * @param params 参数 * @return * @throws Exception */ public static String httpPost(String url, Map<String, Object> params) throws Exception { // 转换请求参数 List<NameValuePair> pairs = covertParams2NVPS(params); HttpPost httpPost = new HttpPost(url); // 设置请求参数 httpPost.setEntity(new UrlEncodedFormEntity(pairs, StandardCharsets.UTF_8.name())); return doHttp(httpPost); } /** * 发送 HTTP POST请求 * <p>带请求参数和请求头</p> * * @param url 地址 * @param headers 请求头 * @param params 参数 * @return * @throws Exception */ public static String httpPost(String url, Map<String, Object> headers, Map<String, Object> params) throws Exception { HttpPost httpPost = new HttpPost(url); // 设置请求参数 StringEntity entity = new StringEntity(JSON.toJSONString(params), ContentType.APPLICATION_JSON); httpPost.setEntity(entity); // 设置请求头 for (Map.Entry<String, Object> param : headers.entrySet()){ httpPost.addHeader(param.getKey(), String.valueOf(param.getValue()));} return doHttp(httpPost); } /** * 转换请求参数 * * @param params * @return */ public static List<NameValuePair> covertParams2NVPS(Map<String, Object> params) { List<NameValuePair> pairs = new ArrayList<NameValuePair>(); for (Map.Entry<String, Object> param : params.entrySet()){ pairs.add(new BasicNameValuePair(param.getKey(), String.valueOf(param.getValue())));} return pairs; } /** * 发送 HTTP 请求 * * @param request * @return * @throws Exception */ private static String doHttp(HttpRequestBase request) throws Exception { // 通过连接池获取连接对象 CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(cm).build(); return doRequest(httpClient, request); } /** * 发送 HTTPS 请求 * <p>使用指定的证书文件及密码</p> * * @param request * @param path * @param password * @return * @throws Exception * @throws Exception */ private static String doHttps(HttpRequestBase request, String path, String password) throws Exception { // 获取HTTPS SSL证书 SSLConnectionSocketFactory csf = getSSLFactory(path, password); // 通过连接池获取连接对象 CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(csf).build(); return doRequest(httpClient, request); } /** * 获取HTTPS SSL连接工厂 * <p>使用指定的证书文件及密码</p> * * @param path 证书全路径 * @param password 证书密码 * @return * @throws Exception * @throws Exception */ private static SSLConnectionSocketFactory getSSLFactory(String path, String password) throws Exception { // 初始化证书,指定证书类型为“PKCS12” KeyStore keyStore = KeyStore.getInstance("PKCS12"); // 读取指定路径的证书 FileInputStream input = new FileInputStream(new File(path)); try { // 装载读取到的证书,并指定证书密码 keyStore.load(input, password.toCharArray()); } finally { input.close(); } // 获取HTTPS SSL证书连接上下文 SSLContext sslContext = SSLContexts.custom().loadKeyMaterial(keyStore, password.toCharArray()).build(); // 获取HTTPS连接工厂,指定TSL版本 SSLConnectionSocketFactory sslCsf = new SSLConnectionSocketFactory(sslContext, new String[]{"SSLv2Hello", "SSLv3", "TLSv1", "TLSv1.2"}, null, SSLConnectionSocketFactory.getDefaultHostnameVerifier()); return sslCsf; } /** * 获取HTTPS SSL连接工厂 * <p>跳过证书校验,即信任所有证书</p> * * @return * @throws Exception */ private static SSLConnectionSocketFactory getSSLFactory() throws Exception { // 设置HTTPS SSL证书信息,跳过证书校验,即信任所有证书请求HTTPS SSLContextBuilder sslBuilder = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() { public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException { return true; } }); // 获取HTTPS SSL证书连接上下文 SSLContext sslContext = sslBuilder.build(); // 获取HTTPS连接工厂 SSLConnectionSocketFactory sslCsf = new SSLConnectionSocketFactory(sslContext, new String[]{"SSLv2Hello", "SSLv3", "TLSv1", "TLSv1.2"}, null, NoopHostnameVerifier.INSTANCE); return sslCsf; } /** * 获取 HTTPClient注册器 * * @return * @throws Exception */ private static Registry<ConnectionSocketFactory> getRegistry() { Registry<ConnectionSocketFactory> registry = null; try { registry = RegistryBuilder.<ConnectionSocketFactory>create().register("http", new PlainConnectionSocketFactory()).register("https", getSSLFactory()).build(); } catch (Exception e) { log.error("获取 HTTPClient注册器失败", e); } return registry; } /** * 处理Http/Https请求,并返回请求结果 * <p>注:默认请求编码方式 UTF-8</p> * * @param httpClient * @param request * @return * @throws Exception */ private static String doRequest(CloseableHttpClient httpClient, HttpRequestBase request) throws Exception { String result = null; CloseableHttpResponse response = null; try { // 获取请求结果 response = httpClient.execute(request); // 解析请求结果 HttpEntity entity = response.getEntity(); // 转换结果 result = EntityUtils.toString(entity, StandardCharsets.UTF_8.name()); // 关闭IO流 EntityUtils.consume(entity); } finally { if (null != response){ response.close();} } return result; } }
package com.sf.vsolution.hb.sfce.util.wechat.face; import org.springframework.web.multipart.MultipartFile; import sun.misc.BASE64Decoder; import sun.misc.BASE64Encoder; import java.io.*; /** * @description: 图片和Base64字符串转换 * @author: zhucj * @date: 2019-10-21 17:41 */ public class Base64ConvertUtils { /** * @Description: 图片转化成base64字符串 * @param: path 路径 * @Return: */ public static String getImageStr(String path) { //将图片文件转化为字节数组字符串,并对其进行Base64编码处理 //待处理的图片 String imgFile = path; InputStream in = null; byte[] data = null; //读取图片字节数组 try { in = new FileInputStream(imgFile); data = new byte[in.available()]; in.read(data); in.close(); } catch (IOException e) { e.printStackTrace(); } //对字节数组Base64编码 BASE64Encoder encoder = new BASE64Encoder(); //返回Base64编码过的字节数组字符串 return encoder.encode(data); } /** * @Description: 图片转化成base64字符串 * @param: file 类型 * @Return: */ public static String getImageStr(MultipartFile file) { //待处理的图片 InputStream in = null; byte[] data = null; //读取图片字节数组 try { in = file.getInputStream(); data = new byte[in.available()]; in.read(data); in.close(); } catch (IOException e) { e.printStackTrace(); } //对字节数组Base64编码 BASE64Encoder encoder = new BASE64Encoder(); //返回Base64编码过的字节数组字符串 return encoder.encode(data); } /** * @Description: base64字符串转化成图片 * @param: imgStr * @Return: */ public static boolean GenerateImage(String imgStr,String photoname) { //对字节数组字符串进行Base64解码并生成图片 //图像数据为空 if (imgStr == null){ return false; } BASE64Decoder decoder = new BASE64Decoder(); try { //Base64解码 byte[] b = decoder.decodeBuffer(imgStr); for(int i=0;i<b.length;++i) { if(b[i]<0) { //调整异常数据 b[i]+=256; } } //生成jpeg图片 String imagePath= "E:/upload/img"; //System.currentTimeMillis() //新生成的图片 String imgFilePath = imagePath+photoname; OutputStream out = new FileOutputStream(imgFilePath); out.write(b); out.flush(); out.close(); return true; } catch (Exception e) { return false; } } }
package com.sf.vsolution.hb.sfce.util.wechat.face; /** * @description: 腾讯云 API服务名称 * @author: zhucj * @date: 2019-10-18 15:12 */ public class AFSServerApi { /** * 创建人员库 */ public static final String SERVER_NAME_API = "https://iai.tencentcloudapi.com"; }
package com.sf.vsolution.hb.sfce.util.wechat.face; /** * @description: * @author:zhucj * @date: 2019-10-21 15:02 */ public enum ContentTypeEnum { JSON("application/json; charset=utf-8"), MULTIPART("multipart/form-data"); private String name; ContentTypeEnum(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
package com.sf.vsolution.hb.sfce.util.wechat.face; /** * @description: * @author: zhucj * @date: 2019-10-21 14:44 */ public enum HttpMethodEnum { GET("GET"), HEAD("HEAD"), POST("POST"), PUT("PUT"), PATCH("PATCH"), DELETE("DELETE"), OPTIONS("OPTIONS"), TRACE("TRACE"); ; private String name; HttpMethodEnum(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
package com.sf.vsolution.hb.sfce.util.wechat.face; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.ToString; import java.io.Serializable; /** * 返回类型 * @author choleece * @date 2018/9/27 */ @ApiModel @ToString public class R<T> implements Serializable { private static final long serialVersionUID = -6287952131441663819L; /** * 编码 */ @ApiModelProperty(value = "响应码", example = "200") private int code = 200; /** * 成功标志 */ @ApiModelProperty(value = "成功标志", example = "true") private Boolean success; /** * 返回消息 */ @ApiModelProperty(value = "返回消息说明", example = "操作成功") private String msg="操作成功"; /** * 返回数据 */ @ApiModelProperty(value = "返回数据") private T data; /** * 创建实例 * @return */ public static R instance() { return new R(); } public int getCode() { return code; } public R setCode(int code) { this.code = code; return this; } public Boolean getSuccess() { return success; } public R setSuccess(Boolean success) { this.success = success; return this; } public String getMsg() { return msg; } public R setMsg(String msg) { this.msg = msg; return this; } public T getData() { return data; } public R setData(T data) { this.data = data; return this; } public static R ok() { return R.instance().setSuccess(true); } public static R ok(Object data) { return ok().setData(data); } public static R ok(Object data, String msg) { return ok(data).setMsg(msg); } public static R error() { return R.instance().setSuccess(false); } public static R error(String msg) { return error().setMsg(msg); } /** * 无参 */ public R() { } public R(int code, String msg) { this.code = code; this.msg = msg; } public R(int code, T data){ this.code = code; this.data = data; } /** * 有全参 * @param code * @param msg * @param data * @param success */ public R(int code, String msg, T data, Boolean success) { this.code = code; this.msg = msg; this.data = data; this.success = success; } /** * 有参 * @param code * @param msg * @param data */ public R(int code, String msg, T data) { this.code = code; this.msg = msg; this.data = data; } }
package com.sf.vsolution.hb.sfce.util.wechat.face; /** * @description: 签名方法 * @author: zhucj * @date: 2019-10-21 13:58 */ public enum SignMenodEnum { HMACSHA1("HmacSHA1"), HMACSHA256("HmacSHA256"), TC3_HMAC_SHA256("TC3-HMAC-SHA256"); private String mendoName; SignMenodEnum(String mendoName) { this.mendoName = mendoName; } public String getMendoName() { return mendoName; } public void setMendoName(String mendoName) { this.mendoName = mendoName; } }
package com.sf.vsolution.hb.sfce.util.wechat.face; import com.alibaba.fastjson.JSON; import lombok.extern.slf4j.Slf4j; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import javax.xml.bind.DatatypeConverter; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Objects; import java.util.TimeZone; import java.util.TreeMap; /** * @description: 腾讯云 签名方法 * @author: zhucj * @date: 2019-10-18 14:14 */ @Slf4j public class SignUtils { private final static String CHARSET = "UTF-8"; private final static Charset UTF8 = StandardCharsets.UTF_8; public static String sign(TreeMap<String, Object> params,HttpMethodEnum menth,SignMenodEnum signMenodEnum, String jsonString,String reqUrl,String sercretKey,ContentTypeEnum typeEnum) throws Exception { String signString = null; String sercretId = String.valueOf(params.get("SecretId")); switch (signMenodEnum){ case TC3_HMAC_SHA256: String replace = reqUrl.replace("https://", ""); String service = replace.substring(0,3); String host = replace; SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); // 注意时区,否则容易出错 sdf.setTimeZone(TimeZone.getTimeZone("UTC")); String date = sdf.format(new Date(Long.valueOf(params.get("Timestamp") + "000"))); // ************* 步骤 1:拼接规范请求串 ************* String canonicalUri = "/"; String canonicalQueryString = ""; String canonicalHeaders = "content-type:"+typeEnum.getName() +"\n" + "host:" + host + "\n"; String signedHeaders = "content-type;host"; String hashedRequestPayload = sha256Hex(jsonString); String canonicalRequest = menth.getName() + "\n" + canonicalUri + "\n" + canonicalQueryString + "\n" + canonicalHeaders + "\n" + signedHeaders + "\n" + hashedRequestPayload; // ************* 步骤 2:拼接待签名字符串 ************* String credentialScope = date + "/" + service + "/" + "tc3_request"; String hashedCanonicalRequest = sha256Hex(canonicalRequest); String stringToSign = signMenodEnum.getMendoName() + "\n" + params.get("Timestamp") + "\n" + credentialScope + "\n" + hashedCanonicalRequest; log.info("待签名参数:{}",stringToSign); // ************* 步骤 3:计算签名 ************* byte[] secretDate = hmac256(("TC3" +sercretKey).getBytes(UTF8), date); byte[] secretService = hmac256(secretDate, service); byte[] secretSigning = hmac256(secretService, "tc3_request"); String signature = DatatypeConverter.printHexBinary(hmac256(secretSigning, stringToSign)).toLowerCase(); log.info("生成签名参数:{}",signature); // ************* 步骤 4:拼接 Authorization ************* String authorization = signMenodEnum.getMendoName() + " " + "Credential=" + sercretId + "/" + credentialScope + ", " + "SignedHeaders=" + signedHeaders + ", " + "Signature=" + signature; log.info("生成authorization参数:{}",authorization); TreeMap<String, String> headers = new TreeMap<String, String>(); headers.put("Authorization", authorization); headers.put("Content-Type",typeEnum.getName()); headers.put("Host", host); headers.put("X-TC-Action",String.valueOf(params.get("Action")) ); headers.put("X-TC-Timestamp",String.valueOf(params.get("Timestamp"))); headers.put("X-TC-Version",String.valueOf(params.get("Version"))); if (Objects.nonNull(params.get("Region"))){ headers.put("X-TC-Region",String.valueOf(params.get("Region"))); } signString = JSON.toJSONString(headers); break; default: StringBuilder s2s = new StringBuilder(reqUrl.replace("https://",menth.getName())+"/?"); // 签名时要求对参数进行字典排序,此处用TreeMap保证顺序 for (String k : params.keySet()) { s2s.append(k).append("=").append(params.get(k).toString()).append("&"); } String s = s2s.toString().substring(0, s2s.length() - 1); Mac mac = Mac.getInstance(signMenodEnum.getMendoName()); SecretKeySpec secretKeySpec = new SecretKeySpec(sercretKey.getBytes(CHARSET), mac.getAlgorithm()); mac.init(secretKeySpec); byte[] hash = mac.doFinal(s.getBytes(CHARSET)); signString = DatatypeConverter.printBase64Binary(hash); break; } return signString ; } /** * 获取签名之后的请求Url * @param params * @return * @throws UnsupportedEncodingException */ public static String getUrl(TreeMap<String, Object> params,String reqUrl) throws UnsupportedEncodingException { StringBuilder url = new StringBuilder(reqUrl+"/?"); // 实际请求的url中对参数顺序没有要求 for (String k : params.keySet()) { // 需要对请求串进行urlencode,由于key都是英文字母,故此处仅对其value进行urlencode url.append(k).append("=").append(URLEncoder.encode(params.get(k).toString(), CHARSET)).append("&"); } return url.toString().substring(0, url.length() - 1); } public static String getUrl(TreeMap<String, Object> params,String reqUrl,String jsonString,ContentTypeEnum typeEnum){ String replace = reqUrl.replace("https://", ""); StringBuilder sb = new StringBuilder(); sb.append("curl -X POST https://").append(replace) .append(" -H \"Authorization: ").append(params.get("Authorization")).append("\"") .append(" -H \"Content-Type:").append(typeEnum.getName()) .append(" -H \"Host: ").append(replace).append("\"") .append(" -H \"X-TC-Action: ").append(params.get("Action")).append("\"") .append(" -H \"X-TC-Timestamp: ").append(params.get("Timestamp")).append("\"") .append(" -H \"X-TC-Version: ").append(params.get("Version")).append("\""); if (Objects.nonNull(params.get("Region"))){ sb.append(" -H \"X-TC-Region: ").append(params.get("Region")).append("\""); } sb.append(" -d '").append(jsonString).append("'"); return sb.toString(); } public 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)); } public 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(); } }
package com.sf.vsolution.hb.sfce.util.wechat.face; /** * @ClassName SystemConstants * @Description 常量字段 * @Author YangLei * @Date 2019/5/7 11:13 * @Version 1.0 **/ public class SystemConstants { /** 传参不规范,code:400*/ public static final Integer PARAM_INCORRECT_CODE = 400; /** 成功,code:200*/ public static final Integer SUCCESS_CODE = 200; /** 服务内部调用失败,code:500*/ public static final Integer SERVER_ERROR_CODE = 500; /** 登录失效,code:401*/ public static final Integer AUTH_FAIL_CODE = 401; /** 无对应接口权限,code:402*/ public static final Integer HAVE_NOT_PERMISSION_CODE = 402; /** 操作无记录,code:403*/ public static final Integer NO_RECORD_OPERATION = 403; }
---------------------------
请求参数和响应参数
package com.sf.vsolution.hb.sfce.util.wechat.face.req; import lombok.Builder; import lombok.Data; /** * @description: 增加人脸 * @author: zhucj * @date: 2019-10-22 15:04 */ @Data @Builder public class CreateFace { private String personId; private String img01; private String img02; private String img03; private String img04; private String img05; }
package com.sf.vsolution.hb.sfce.util.wechat.face.req; import lombok.Builder; import lombok.Data; /** * @description: 创建人脸请求参数 * @author: zhucj * @date: 2019-10-18 15:30 */ @Data @Builder public class CreateGroup extends PublicParam { /** * 人员库名称,[1,60]个字符,可修改,不可重复 */ private String groupName; /** * 人员库 ID,不可修改,不可重复。支持英文、数字、-%@#&_,长度限制64B */ private String groupId; /** * 人员库自定义描述字段,用于描述人员库中人员属性,该人员库下所有人员将拥有此描述字段。 * 最多可以创建5个。 * 每个自定义描述字段支持[1,30]个字符。 * 在同一人员库中自定义描述字段不可重复。 * 例: 设置某人员库“自定义描述字段”为["学号","工号","手机号"], * 则该人员库下所有人员将拥有名为“学号”、“工号”、“手机号”的描述字段, * 可在对应人员描述字段中填写内容,登记该人员的学号、工号、手机号等信息。 */ private String groupExDescriptions01; private String groupExDescriptions02; private String groupExDescriptions03; /** * 人员库信息备注,[0,40]个字符 */ private String tag; }
package com.sf.vsolution.hb.sfce.util.wechat.face.req; import lombok.Builder; import lombok.Data; /** * @description: 人员请求参数 * @author: zhucj * @date: 2019-10-21 16:15 */ @Data @Builder public class CreatePerson { private String groupId; private String personName; private String personId; private Integer gender; private String personExDescription01; private String personExDescription02; private String personExDescription03; private String image; private String url; }
package com.sf.vsolution.hb.sfce.util.wechat.face.req; import lombok.Data; /** * @description: 删除人员库请求参数 * @author:zhucj * @date: 2019-10-18 15:37 */ @Data public class DeleteGroup extends PublicParam{ /** * 人员库ID */ private String groupId; }
package com.sf.vsolution.hb.sfce.util.wechat.face.req; import lombok.Data; /** * @description: 人员库列表请求参数 * @author: zhucj * @date: 2019-10-18 15:39 */ @Data public class GetGroupList extends PublicParam { /** * 起始序号,默认值为0 */ private Integer offset; /** * 返回数量,默认值为10,最大值为1000 */ private Integer limit; }
package com.sf.vsolution.hb.sfce.util.wechat.face.req; import lombok.Data; /** * @description: 获取人员信息 * @author: zhucj * @date: 2019-10-22 13:58 */ @Data public class GetPersonBaseInfo{ private String personId; }
package com.sf.vsolution.hb.sfce.util.wechat.face.req; import lombok.Data; /** * @description: * @author: zhucj * @date: 2019-10-18 15:42 */ @Data public class ModifyGroup extends PublicParam { /** * 人员库ID */ private String groupId; /** * 需要修改的人员库自定义描述字段,key-value */ private String groupExDescriptionInfos01; /** * 需要修改的人员库自定义描述字段,key-value */ private String groupExDescriptionInfosIndex01; /** * 需要修改的人员库自定义描述字段,key-value */ private String groupExDescriptionInfos02; /** * 需要修改的人员库自定义描述字段,key-value */ private String groupExDescriptionInfosIndex02; /** * 人员库信息备注 */ private String tag; }
package com.sf.vsolution.hb.sfce.util.wechat.face.req; import lombok.Data; /** * @description: 通用公共请求参数 * @author:zhucj * @date: 2019-10-18 15:35 */ @Data public class PublicParam { private String action; private String version; private String region; }
package com.sf.vsolution.hb.sfce.util.wechat.face.resp; import lombok.Data; /** * @description: * @author:zhucj * @date: 2019-10-21 17:08 */ @Data public class CreatePersonResp extends Response { private String FaceId; }
package com.sf.vsolution.hb.sfce.util.wechat.face.resp; import lombok.Data; /** * @description: * @author:zhucj * @date: 2019-10-18 15:58 */ @Data public class Error { private String Code; private String Messag; }
package com.sf.vsolution.hb.sfce.util.wechat.face.resp; import lombok.Data; import java.util.List; /** * @description: * @author: zhucj * @date: 2019-10-18 16:02 */ @Data public class GetGroupListResp extends Response { private String GroupNum; private List<GroupInfos> GroupInfos; }
package com.sf.vsolution.hb.sfce.util.wechat.face.resp; import lombok.Data; /** * @description: * @author: zhucj * @date: 2019-10-22 14:01 */ @Data public class GetPersonBaseInfoResp extends Response { private String PersonName; private String Gender; private String[] FaceIds; }
package com.sf.vsolution.hb.sfce.util.wechat.face.resp; import lombok.Data; /** * @description: * @author:zhucj * @date: 2019-10-18 16:00 */ @Data public class GroupInfos { private String GroupName; private String GroupId; private String[] GroupExDescriptions; private String Tag; }
package com.sf.vsolution.hb.sfce.util.wechat.face.resp; import lombok.Data; /** * @description: 请求返回公共参数 * @author: zhucj * @date: 2019-10-18 15:56 */ @Data public class Response { private String RequestId; private Error Error; }
package com.sf.vsolution.hb.sfce.util.wechat.face.resp; import lombok.Data; /** * @description: * @author: zhucj * @date: 2019-10-22 17:27 */ @Data public class VerifyFaceResp extends Response { private String Score; private Boolean IsMatch; }
--------------------------------------------
Yml配置文件
#腾讯云服务配置 tencent: secretId: ********* secretKey: ********
依赖Maven包
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.6.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.6.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.55</version>
</dependency>
<!--httpclient -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.6</version>
</dependency>