javaweb如何使用華為云短信通知公共類調(diào)用
javaweb華為云短信通知公共類調(diào)用
情景:公司業(yè)務(wù)需求,短信從阿里云切換到華為云,參照華為云短信調(diào)用的相關(guān)文檔遇到不少坑,在此記錄一下。
開發(fā)環(huán)境:JDK1.8 系統(tǒng)環(huán)境:SpringBoot
1、華為云短信配置信息在application.yml中配置
sms: huawei: url: https://rtcsms.cn-north-1.myhuaweicloud.com:10743/sms/batchSendSms/v1 appKey: ****** appSecret: ******
2、創(chuàng)建短信公共類:HwSmsSender
package com.seeker.common.utils.hwsms; import com.alibaba.fastjson.JSONObject; import com.seeker.common.utils.Constants; import com.seeker.common.utils.LoggerUtils; import com.seeker.common.utils.SmsConstants; import org.apache.commons.codec.binary.Hex; import org.apache.commons.codec.digest.DigestUtils; import org.apache.http.HttpHeaders; import org.apache.http.HttpResponse; import org.apache.http.NameValuePair; import org.apache.http.client.methods.RequestBuilder; import org.apache.http.client.utils.URLEncodedUtils; import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.message.BasicNameValuePair; import org.apache.http.ssl.SSLContextBuilder; import org.apache.http.util.EntityUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import java.nio.charset.Charset; import java.text.SimpleDateFormat; import java.util.*; @Component public class HwSmsSender { protected Logger logger = LoggerFactory.getLogger(getClass()); /** * 無需修改,用于格式化鑒權(quán)頭域,給"X-WSSE"參數(shù)賦值 */ private static final String WSSE_HEADER_FORMAT = "UsernameToken Username=\"%s\",PasswordDigest=\"%s\",Nonce=\"%s\",Created=\"%s\""; /** * 無需修改,用于格式化鑒權(quán)頭域,給"Authorization"參數(shù)賦值 */ private static final String AUTH_HEADER_VALUE = "WSSE realm=\"SDP\",profile=\"UsernameToken\",type=\"Appkey\""; @Value("${sms.huawei.url}") private String url; @Value("${sms.huawei.appKey}") private String appKey; @Value("${sms.huawei.appSecret}") private String appSecret; public String sendNotifyMsg(String mobile, String templateId, String templateParas) throws Exception { // 默認(rèn)通知類 return sendMsg(mobile, SmsConstants.ResultMessage.SIGN_NOTIFY_ID, templateId, templateParas); } public String sendMsg(String mobile, String sender, String templateId, String templateParas) throws Exception { //條件必填,國內(nèi)短信關(guān)注,當(dāng)templateId指定的模板類型為通用模板時(shí)生效且必填,必須是已審核通過的,與模板類型一致的簽名名稱 //國際/港澳臺(tái)短信不用關(guān)注該參數(shù) //簽名名稱 String signature = "美團(tuán)外賣"; // String sender = "10690400999304584"; String receiver = "+86" + mobile; //選填,短信狀態(tài)報(bào)告接收地址,推薦使用域名,為空或者不填表示不接收狀態(tài)報(bào)告 String statusCallBack = ""; /** * 選填,使用無變量模板時(shí)請(qǐng)賦空值 String templateParas = ""; * 單變量模板示例:模板內(nèi)容為"您的驗(yàn)證碼是${1}"時(shí),templateParas可填寫為"[\"369751\"]" * 雙變量模板示例:模板內(nèi)容為"您有${1}件快遞請(qǐng)到${2}領(lǐng)取"時(shí),templateParas可填寫為"[\"3\",\"人民公園正門\"]" * 模板中的每個(gè)變量都必須賦值,且取值不能為空 * 查看更多模板和變量規(guī)范:產(chǎn)品介紹>模板和變量規(guī)范 */ //模板變量,此處以單變量驗(yàn)證碼短信為例,請(qǐng)客戶自行生成6位驗(yàn)證碼,并定義為字符串類型,以杜絕首位0丟失的問題(例如:002569變成了2569)。 //templateParas = "[\"369751\"]"; //請(qǐng)求Body,不攜帶簽名名稱時(shí),signature請(qǐng)?zhí)頽ull String body = buildRequestBody(sender, receiver, templateId, templateParas, statusCallBack, signature); if (null == body || body.isEmpty()) { LoggerUtils.info(logger, "body is null."); return "1"; } //請(qǐng)求Headers中的X-WSSE參數(shù)值 String wsseHeader = buildWsseHeader(appKey, appSecret); if (null == wsseHeader || wsseHeader.isEmpty()) { LoggerUtils.info(logger, "wsse header is null."); return "1"; } //如果JDK版本低于1.8,可使用如下代碼 //為防止因HTTPS證書認(rèn)證失敗造成API調(diào)用失敗,需要先忽略證書信任問題 //CloseableHttpClient client = HttpClients.custom() // .setSSLContext(new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() { // @Override // public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { // return true; // } // }).build()).setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE).build(); //如果JDK版本是1.8,可使用如下代碼 //為防止因HTTPS證書認(rèn)證失敗造成API調(diào)用失敗,需要先忽略證書信任問題 CloseableHttpClient client = HttpClients.custom() .setSSLContext(new SSLContextBuilder().loadTrustMaterial(null, (x509CertChain, authType) -> true).build()) .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE) .build(); //請(qǐng)求方法POST HttpResponse response = client.execute(RequestBuilder.create("POST") .setUri(url) .addHeader(HttpHeaders.CONTENT_TYPE, "application/x-www-form-urlencoded") .addHeader(HttpHeaders.AUTHORIZATION, AUTH_HEADER_VALUE) .addHeader("X-WSSE", wsseHeader) .setEntity(new StringEntity(body)).build()); //打印響應(yīng)頭域信息 LoggerUtils.info(logger, (response.toString())); //打印響應(yīng)消息實(shí)體 String entity = EntityUtils.toString(response.getEntity()); LoggerUtils.info(logger, "消息實(shí)體" + entity); HwSmsRoot hwSmsRoot = JSONObject.parseObject(entity, HwSmsRoot.class); return "0"; } /** * 構(gòu)造請(qǐng)求Body體 * * @param sender * @param receiver * @param templateId * @param templateParas * @param statusCallbackUrl * @param signature | 簽名名稱,使用國內(nèi)短信通用模板時(shí)填寫 * @return */ static String buildRequestBody(String sender, String receiver, String templateId, String templateParas, String statusCallbackUrl, String signature) { if (null == sender || null == receiver || null == templateId || sender.isEmpty() || receiver.isEmpty() || templateId.isEmpty()) { System.out.println("buildRequestBody(): sender, receiver or templateId is null."); return null; } List<NameValuePair> keyValues = new ArrayList<NameValuePair>(); keyValues.add(new BasicNameValuePair("from", sender)); keyValues.add(new BasicNameValuePair("to", receiver)); keyValues.add(new BasicNameValuePair("templateId", templateId)); if (null != templateParas && !templateParas.isEmpty()) { keyValues.add(new BasicNameValuePair("templateParas", templateParas)); } if (null != statusCallbackUrl && !statusCallbackUrl.isEmpty()) { keyValues.add(new BasicNameValuePair("statusCallback", statusCallbackUrl)); } if (null != signature && !signature.isEmpty()) { keyValues.add(new BasicNameValuePair("signature", signature)); } return URLEncodedUtils.format(keyValues, Charset.forName("UTF-8")); } /** * 構(gòu)造X-WSSE參數(shù)值 * * @param appKey * @param appSecret * @return */ static String buildWsseHeader(String appKey, String appSecret) { if (null == appKey || null == appSecret || appKey.isEmpty() || appSecret.isEmpty()) { System.out.println("buildWsseHeader(): appKey or appSecret is null."); return null; } SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); //Created String time = sdf.format(new Date()); //Nonce String nonce = UUID.randomUUID().toString().replace("-", ""); byte[] passwordDigest = DigestUtils.sha256(nonce + time + appSecret); String hexDigest = Hex.encodeHexString(passwordDigest); //如果JDK版本是1.8,請(qǐng)加載原生Base64類,并使用如下代碼 //PasswordDigest String passwordDigestBase64Str = Base64.getEncoder().encodeToString(hexDigest.getBytes()); //如果JDK版本低于1.8,請(qǐng)加載三方庫提供Base64類,并使用如下代碼 //PasswordDigest //String passwordDigestBase64Str = Base64.encodeBase64String(hexDigest.getBytes(Charset.forName("utf-8"))); //若passwordDigestBase64Str中包含換行符,請(qǐng)執(zhí)行如下代碼進(jìn)行修正 //passwordDigestBase64Str = passwordDigestBase64Str.replaceAll("[\\s*\t\n\r]", ""); return String.format(WSSE_HEADER_FORMAT, appKey, passwordDigestBase64Str, nonce, time); } //本地調(diào)試方法 public static void main(String[] args) throws Exception { //必填,請(qǐng)參考"開發(fā)準(zhǔn)備"獲取如下數(shù)據(jù),替換為實(shí)際值 //APP接入地址+接口訪問URI String url = "https://rtcsms.cn-north-1.myhuaweicloud.com:10743/sms/batchSendSms/v1"; //APP_Key 輸入自己的 String appKey = "******"; //APP_Secret 輸入自己的 String appSecret = "******"; //國內(nèi)短信簽名通道號(hào)或國際/港澳臺(tái)短信通道號(hào) String sender = "8820032023657"; //模板ID String templateId = "d1e8f2c7ab964c6998bda5638238bb7d"; //條件必填,國內(nèi)短信關(guān)注,當(dāng)templateId指定的模板類型為通用模板時(shí)生效且必填,必須是已審核通過的,與模板類型一致的簽名名稱 //國際/港澳臺(tái)短信不用關(guān)注該參數(shù) //簽名名稱 String signature = "美團(tuán)外賣"; //必填,全局號(hào)碼格式(包含國家碼),示例:+8615123**6789,多個(gè)號(hào)碼之間用英文逗號(hào)分隔 //String receiver = "+8615123**6789,+8615234**7890"; //短信接收人號(hào)碼 String receiver = "+8617520**2687"; //選填,短信狀態(tài)報(bào)告接收地址,推薦使用域名,為空或者不填表示不接收狀態(tài)報(bào)告 String statusCallBack = ""; /** * 選填,使用無變量模板時(shí)請(qǐng)賦空值 String templateParas = ""; * 單變量模板示例:模板內(nèi)容為"您的驗(yàn)證碼是${1}"時(shí),templateParas可填寫為"[\"369751\"]" * 雙變量模板示例:模板內(nèi)容為"您有${1}件快遞請(qǐng)到${2}領(lǐng)取"時(shí),templateParas可填寫為"[\"3\",\"人民公園正門\"]" * 模板中的每個(gè)變量都必須賦值,且取值不能為空 * 查看更多模板和變量規(guī)范:產(chǎn)品介紹>模板和變量規(guī)范 */ //模板變量,此處以單變量驗(yàn)證碼短信為例,請(qǐng)客戶自行生成6位驗(yàn)證碼,并定義為字符串類型,以杜絕首位0丟失的問題(例如:002569變成了2569)。 String templateParas = JSONObject.toJSONString(new String[]{"598745", "1"}); //請(qǐng)求Body,不攜帶簽名名稱時(shí),signature請(qǐng)?zhí)頽ull String body = buildRequestBody(sender, receiver, templateId, templateParas, statusCallBack, signature); if (null == body || body.isEmpty()) { System.out.println("body is null."); return; } //請(qǐng)求Headers中的X-WSSE參數(shù)值 String wsseHeader = buildWsseHeader(appKey, appSecret); if (null == wsseHeader || wsseHeader.isEmpty()) { System.out.println("wsse header is null."); return; } //如果JDK版本低于1.8,可使用如下代碼 //為防止因HTTPS證書認(rèn)證失敗造成API調(diào)用失敗,需要先忽略證書信任問題 //CloseableHttpClient client = HttpClients.custom() // .setSSLContext(new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() { // @Override // public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { // return true; // } // }).build()).setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE).build(); //如果JDK版本是1.8,可使用如下代碼 //為防止因HTTPS證書認(rèn)證失敗造成API調(diào)用失敗,需要先忽略證書信任問題 CloseableHttpClient client = HttpClients.custom() .setSSLContext(new SSLContextBuilder().loadTrustMaterial(null, (x509CertChain, authType) -> true).build()) .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE) .build(); //請(qǐng)求方法POST HttpResponse response = client.execute(RequestBuilder.create("POST") .setUri(url) .addHeader(HttpHeaders.CONTENT_TYPE, "application/x-www-form-urlencoded") .addHeader(HttpHeaders.AUTHORIZATION, AUTH_HEADER_VALUE) .addHeader("X-WSSE", wsseHeader) .setEntity(new StringEntity(body)).build()); //打印響應(yīng)頭域信息 System.out.println(response.toString()); //打印響應(yīng)消息實(shí)體 String entity = EntityUtils.toString(response.getEntity()); System.out.println(entity); HwSmsRoot hwSmsRoot = JSONObject.parseObject(entity, HwSmsRoot.class); System.out.println(hwSmsRoot.getCode()); } }
3、上面公共類還用到以下類:
HwSmsResult
import lombok.Data; @Data public class HwSmsResult { private String originTo; private String createTime; private String from; private String smsMsgId; private String status; }
HwSmsRoot
@Data public class HwSmsRoot { private List<HwSmsResult> result; private String code; private String description; }
SmsConstants
public class SmsConstants { public static class ResultMessage { /** * 通道 美團(tuán)外賣 驗(yàn)證碼類 */ public static String SIGN_NOTIFY_ID = "8821041535507"; public static String SMS_SEND_ID = "8fed7b85f85d4a248fe983a321"; } }
LoggerUtils
import org.slf4j.Logger; public class LoggerUtils { public static void debug(Logger logger, Object message) { if (logger.isDebugEnabled()) { logger.debug("Debug日志信息{}", message); } } public static void info(Logger logger, Object message) { if (logger.isInfoEnabled()) { logger.info("Info日志信息{}", message); } } public static void error(Logger logger, Object message) { if (logger.isErrorEnabled()) { logger.error("Error日志信息{}", message); } } }
4、實(shí)戰(zhàn)調(diào)用如下:
/** * 獲取驗(yàn)證碼接口 */ @PostMapping("/sendMessageWxUser") public AjaxResult sendMessageWxUser(@RequestBody WxUser user, HttpServletRequest request, HttpServletResponse response) throws Exception { String ipAddr = IPUtils.getIpAddr(request); AjaxResult ajaxResult = new AjaxResult(0, "短信發(fā)送成功!請(qǐng)等待查收!"); if (StringUtils.isEmpty(user.getLoginName())) { return AjaxResult.error(500, "手機(jī)號(hào)不能為空"); } //生成6位隨機(jī)數(shù),有效期1分鐘 String randNum = VerificatieCodeUtils.getRandNum(6); String[] templateParas = new String[]{randNum, "1"}; String templateId = SmsConstants.ResultMessage.SMS_SEND_ID; String code = smsSendUtils.sendRegisterSMS(user.getLoginName(), templateId, JSONObject.toJSONString(templateParas)); logger.info("--打印驗(yàn)證碼:" + randNum); if ("0".equals(code)) { //緩存按ip+varcode 作為key保存 redisCache.setCacheObject(ipAddr + "varcode", randNum, 60, TimeUnit.SECONDS); return ajaxResult; } else { return new AjaxResult(500, "短信發(fā)送失??!"); } }
VerificatieCodeUtils :驗(yàn)證碼隨機(jī)數(shù)生成
import java.util.Random; public class VerificatieCodeUtils { public static String getRandNum(int charCount) { String charValue = ""; for (int i = 0; i < charCount; i++) { char c = (char) (randomInt(0, 10) + '0'); charValue += String.valueOf(c); } return charValue; } public static int randomInt(int from, int to) { Random r = new Random(); return from + r.nextInt(to - from); } }
最后:需要注意的是,根據(jù)自己項(xiàng)目開發(fā)環(huán)境版本調(diào)整一些相關(guān)方法,以及華為云一些密鑰什么需要填寫正確。
java調(diào)用華為云上的文字識(shí)別(OCR)接口
首先要滿足的條件,已注冊(cè)華為云賬戶,并訂閱華為云上的文字識(shí)別接口,以下以訂閱的身份證識(shí)別接口為例
package OCRDemo; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.URL; import java.net.URLConnection; import java.util.List; import java.util.Map; /** * @author abang * @date 2020/7/3 0003 20:59 */ public class GetOCR { public static void main(String[] args) throws IOException { //請(qǐng)求的region String region = "cn-north-4"; //用戶名 String username = "xxxxxx"; //用戶密碼 String password = "xxxxxx"; //賬戶名 String userName = "xxxxxx"; //請(qǐng)求的uri接口 String uri = "/v1.0/ocr/id-card"; //傳入image或uri的json字符串(圖片為公網(wǎng)的地址,可查詢) String param = "{\"url\":\"http://photocdn.sohu.com/20101021/Img276166786.jpg\"}"; //獲取用token String token = getToken(region,username,password,userName); //返回請(qǐng)求的圖片文字信息 System.out.println(getIdCard(region,token,param,uri)); } //獲取請(qǐng)求鏈接中用戶的token信息 public static String getToken(String region,String username,String password,String userName) throws IOException { String iam = "https://iam."+region+".myhuaweicloud.com/v3/auth/tokens"; String param = "{\"auth\":{\"identity\":{\"methods\":[\"password\"],\"password\":{\"user\":{\"name\":\"" + username + "\",\"password\":\"" + password + "\",\"domain\":{\"name\":\"" + userName + "\"}}}},\"scope\":{\"project\":{\"name\":\"cn-north-4\"}}}}"; PrintWriter out; BufferedReader in = null; String token = ""; String response = ""; try { //需要請(qǐng)求的url URL url = new URL(iam); //打開和URL之間的連接 URLConnection connection = url.openConnection(); //設(shè)置通用的請(qǐng)求屬性,請(qǐng)求頭部分 connection.setRequestProperty("accept", "*/*"); connection.setRequestProperty("connection", "Keep-Alive"); connection.setRequestProperty("user-agent", "Mozilla/4.0"); // 發(fā)送POST請(qǐng)求必須設(shè)置如下兩行 connection.setDoInput(true); connection.setDoOutput(true); // 建立實(shí)際的連接 connection.connect(); ///獲取URLConnection對(duì)象對(duì)應(yīng)的輸出流 out = new PrintWriter(connection.getOutputStream()); //發(fā)送請(qǐng)求參數(shù) out.write(param); //flush輸出流的緩沖 out.flush(); //獲取相應(yīng)頭中的token信息 token = connection.getHeaderField("X-Subject-Token"); //定義BufferedReader輸入流來讀取URL的響應(yīng) in = new BufferedReader(new InputStreamReader(connection.getInputStream())); String line; while ((line = in.readLine()) != null) { //換行打印獲取結(jié)果 response += "\n" + line; } // 獲取所有響應(yīng)頭字段 Map<String, List<String>> map = connection.getHeaderFields(); // 遍歷所有的響應(yīng)頭字段 for (String key : map.keySet()) { //打印出相應(yīng)頭中的信息 //System.out.println(key + "--->" + map.get(key)); } } catch (Exception e) { System.out.println("發(fā)送GET請(qǐng)求出現(xiàn)異常!" + e); e.printStackTrace(); } // 使用finally塊來關(guān)閉輸入流 finally { try { if (in != null) { in.close(); } } catch (Exception e2) { e2.printStackTrace(); } } //返回所要用到的token信息 return token; } //請(qǐng)求云上接口,調(diào)用接口服務(wù) public static String getIdCard(String region,String token,String param,String uri) throws IOException { String ocr = "https://ocr."+region+".myhuaweicloud.com"+uri; String response = ""; BufferedReader in = null; try { URL url = new URL(ocr); URLConnection connection = url.openConnection(); //容易載跟頭,表明請(qǐng)求體的部分為json形式 connection.setRequestProperty("Content-Type", "application/json"); connection.setRequestProperty("X-Auth-Token", token); connection.setDoOutput(true); connection.setDoInput(true); connection.connect(); PrintWriter out = new PrintWriter(connection.getOutputStream()); out.write(param); out.flush(); in = new BufferedReader(new InputStreamReader(connection.getInputStream())); String line; while ((line = in.readLine()) != null) { response += "\n" + line; } } catch (Exception e) { System.out.println("發(fā)送GET請(qǐng)求出現(xiàn)異常!" + e); e.printStackTrace(); } // 使用finally塊來關(guān)閉輸入流 finally { try { if (in != null) { in.close(); } } catch (Exception e2) { e2.printStackTrace(); } } //返回相應(yīng)體中的結(jié)果,打印出來 return response; } }
返回成功
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java大數(shù)據(jù)開發(fā)Hadoop?MapReduce
MapReduce的思想核心是“分而治之”,適用于大量復(fù)雜的任務(wù)處理場(chǎng)景(大規(guī)模數(shù)據(jù)處理場(chǎng)景)Map負(fù)責(zé)“分”,即把復(fù)雜的任務(wù)分解為若干個(gè)“簡(jiǎn)單的任務(wù)”來并行處理。可以進(jìn)行拆分的前提是這些小任務(wù)可以并行計(jì)算,彼此間幾乎沒有依賴關(guān)系2023-03-03淺談java反射和自定義注解的綜合應(yīng)用實(shí)例
本篇文章主要介紹了java反射和自定義注解的綜合應(yīng)用,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-09-09SpringBoot Actuator潛在的OOM問題的解決
本文主要介紹了SpringBoot Actuator潛在的OOM問題的解決,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-11-11IDEA 2020.1 版自動(dòng)導(dǎo)入MAVEN依賴的方法(新版MAVEN無法自動(dòng)導(dǎo)入/更新POM依賴、MAVEN設(shè)置自動(dòng)更
這篇文章主要介紹了IDEA 2020.1 版自動(dòng)導(dǎo)入MAVEN依賴的方法(新版MAVEN無法自動(dòng)導(dǎo)入/更新POM依賴、MAVEN設(shè)置自動(dòng)更新、自動(dòng)更新快捷鍵),需要的朋友可以參考下2020-08-08SpringBoot集成Redis并實(shí)現(xiàn)主從架構(gòu)的實(shí)踐
本文主要和大家分享一下在springboot中如何集成redis,并實(shí)現(xiàn)主從架構(gòu),進(jìn)行數(shù)據(jù)的簡(jiǎn)單存儲(chǔ),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-12-12教你用Java在個(gè)人電腦上實(shí)現(xiàn)微信掃碼支付
今天給大家?guī)淼氖荍ava實(shí)戰(zhàn)的相關(guān)知識(shí),文章圍繞著Java在個(gè)人電腦上實(shí)現(xiàn)微信掃碼支付展開,文中有非常詳細(xì)的介紹及代碼示例,需要的朋友可以參考下2021-06-06