本次將分享給大家,調用騰訊雲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>
