Java实现短信验证功能其实就是调用接口,通过URL和设置请求参数来访问各个短信平台达到发送短信的目的,不同平台的API有着不同的要求,需要自己去仔细阅读相关的文档和接入实例。
下面介绍一下网易云的短信发送实现(国内比较好找的平台还挺多的比如阿里云,秒滴云,阿里云有着自己造的轮子,已经非常成熟和复杂,同时也有他们自己的http协议能让更多的编程语言快速开发,所以这里不对阿里云实现。秒滴云则是专门面向企业的平台而对于个人开发和学习则可能需要各位按照各自公司的规章制度来。市面上的短信平台API虽然各不相同,但是其底层原理都是大同小异的而网易云的相对比较简单,下面就使用秒滴云中给的Demo来请求网易云的URL发送短信)
首先我们需要注册并登录网易云,然后在控制台找到我们需要的通信服务然后创建应用,开通短信服务并获取短信的App_Key和创建模板并获取模板ID
1.创建一个常量类用来存放我们刚刚获取到的信息,把你获取到信息的替换掉其中的星号,当然这个你也可以不用常量类存储,看个人需求而来。
package com.wzh.util;
/**
* @author Wzh
* @since 2019-4-22
* 根据项目需求把不需要的删掉
*/
public class MessageConfig {
/**
* URL
*/
public static final String BASE_URL = "https://api.netease.im/sms/sendcode.action";
/*===============================网易云==========================================*/
/**
* 网易App_Key
*/
public static final String App_Key = "***********";
/**
* 网易App_Secret
*/
public static final String App_Secret = "***********";
/**
* 短信验证模板ID
*/
public static final String TEMPLATEID="***********";
/**
* 验证码长度
*/
public static final String CODELEN="6";
/*===============================秒滴云==========================================*/
/**
* 系统生成的帐号
*/
public static final String ACCOUNT_SID = "***********";
/**
* 生成的TOKEN
*/
public static final String AUTH_TOKEN = "***********";
/**
* 响应的数据格式
*/
public static final String RESP_TYPE = "JSON";
}
2.创建Util类用来发送请求并设置请求头的参数
package com.wzh.util;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Date;
/**
* @author Wzh
* @since 2019-4-22
*/
public class MessageUtil {
/**
* post请求
*
* @param url 请求地址
* @param body 请求内容
* @return
* @throws IOException
*/
public static String post(String url, String body) throws IOException {
System.out.println("url:" + System.lineSeparator() + url);
System.out.println("body:" + System.lineSeparator() + body);
String result = "";
String curTime = String.valueOf((new Date()).getTime() / 1000L);
// 这个不是验证码,应该是用于请求头的签名和加密,随机验证码是网易云生成的
String NONCE = IndustrySMS.smsCode();
String checkSum = CheckSumBuilder.getCheckSum(MessageConfig.App_Secret, NONCE, curTime);
// 建立连接
try {
OutputStreamWriter out = null;
BufferedReader in = null;
URL realUrl = new URL(url);
URLConnection conn = realUrl.openConnection();
// 设置连接参数
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setConnectTimeout(5000);
conn.setReadTimeout(20000);
// 设置请求头
conn.setRequestProperty("AppKey", MessageConfig.App_Key);
conn.setRequestProperty("Nonce", NONCE);
conn.setRequestProperty("CurTime", curTime);
conn.setRequestProperty("CheckSum", checkSum);
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
// 提交数据
out = new OutputStreamWriter(conn.getOutputStream(), "UTF-8");
out.write(body);
out.flush();
// 读取返回数据
in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
String line = "";
// 读第一行不加换行符
boolean firstLine = true;
while ((line = in.readLine()) != null) {
if (firstLine) {
firstLine = false;
} else {
result += System.lineSeparator();
}
result += line;
}
} catch (MalformedURLException e) {
e.printStackTrace();
}
return result;
}
}
到这里和秒滴云有些不同的地方就在于一个checkSum(应该是用来加密或签名的)的生成和一些请求头的设置(这些请求头设置是根据所用平台的API来设置的),CheckSumBuilder类是网易云官方给我们提供的类可以在他们的API中找到,我会把代码放到后面。
3.创建实现类来发送短信
package com.wzh.util;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
/**
* 验证码短信通知接口
* @author Wzh
* @since 2019-4-22
*/
public class IndustrySMS {
// 要发送的手机号
private static String mobile = "13*******66";
/**
* 验证码通知短信
*/
public static void execute() throws IOException {
String tmpSmsContent = null;
try {
// URLEncode.encode();是将你要传递的参数在post请求以流的方式发送,这里我们需要的参数是模板ID,手机号,验证码长度
tmpSmsContent = "templateid=" + URLEncoder.encode(MessageConfig.TEMPLATEID,"UTF-8");
tmpSmsContent += "&mobile=" + URLEncoder.encode(mobile,"UTF-8");
tmpSmsContent += "&codeLen=" + URLEncoder.encode(MessageConfig.CODELEN,"UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
// 各个平台的接口URL
String url = MessageConfig.BASE_URL;
String body =tmpSmsContent;
String result = MessageUtil.post(url,body);
// 返回结果是一个json数据{"code":200,"msg":"7","obj":"xxx"},我们要用处理json数据的包来将他转换成集合,obj就是我们需要来匹配的验证码
System.out.println("result:" + System.lineSeparator() + result);
}
// 创建随机数
public static String smsCode(){
String random=(int)((Math.random()*9+1)*100000)+"";
return random;
}
}
mobile:要发送的手机号码,可从将它放到参数中,由用户填写来获取。
URLEncoder.encode() 方法将我们要传递的参数以二进制流的形式在post请求中传输,参数之间用&分隔。
获取到的result是一个json格式的数据可以用处理json数据的工具类来将它转换成集合,数据中obj的值就是我们需要匹配的验证码(视各个平台而定)
4.CheckSumBuilder类
package com.wzh.util;
import java.security.MessageDigest;
/**
* @author anyone
*/
public class CheckSumBuilder {
// 计算并获取CheckSum
public static String getCheckSum(String appSecret, String nonce, String curTime) {
return encode("sha1", appSecret + nonce + curTime);
}
// 计算并获取md5值
public static String getMD5(String requestBody) {
return encode("md5", requestBody);
}
private static String encode(String algorithm, String value) {
if (value == null) {
return null;
}
try {
MessageDigest messageDigest
= MessageDigest.getInstance(algorithm);
messageDigest.update(value.getBytes());
return getFormattedText(messageDigest.digest());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static String getFormattedText(byte[] bytes) {
int len = bytes.length;
StringBuilder buf = new StringBuilder(len * 2);
for (int j = 0; j < len; j++) {
buf.append(HEX_DIGITS[(bytes[j] >> 4) & 0x0f]);
buf.append(HEX_DIGITS[bytes[j] & 0x0f]);
}
return buf.toString();
}
private static final char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5',
'6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
}
5.测试类
根据个人项目需求编写
package com.wzh.entity;
import com.wzh.util.IndustrySMS;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.io.IOException;
@RunWith(SpringRunner.class)
@SpringBootTest
public class HgerApplicationTests {
@Test
public void testSms() {
try {
IndustrySMS.execute();
} catch (IOException e) {
e.printStackTrace();
}
}
}
6.总结
Java实现短信验证功能简单来说就是调用外部短信平台接口来开发,我们只要严格的遵守所调用平台所定的规范,那么这个功能是非常容易实现的,所以说多去仔细看看官方文档是肯定没错的。其实在上述网易云的接口调用实例中,网易云官方有着更简单的示例,之所以用这么复杂的方式来实现是为了我们更好的理解其中通信的原理,各个平台之间底层的原理都是一致的,只要我们理解了那么今后当我们需要去学习另外的规范时也能快速掌握。下面将官方的接入示例也展示出来
package com.netease.code;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import com.netease.checksum.CheckSumBuilder;
/**
* 发送验证码
* @author liuxuanlin
*
*/
public class SendCode {
//发送验证码的请求路径URL
private static final String
SERVER_URL="https://api.netease.im/sms/sendcode.action";
//网易云信分配的账号,请替换你在管理后台应用下申请的Appkey
private static final String
APP_KEY="fd460d34e786e7754e505bc4fab0f027";
//网易云信分配的密钥,请替换你在管理后台应用下申请的appSecret
private static final String APP_SECRET="dffdf7757248";
//随机数
private static final String NONCE="123456";
//短信模板ID
private static final String TEMPLATEID="3057527";
//手机号
private static final String MOBILE="13888888888";
//验证码长度,范围4~10,默认为4
private static final String CODELEN="6";
public static void main(String[] args) throws Exception {
DefaultHttpClient httpClient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost(SERVER_URL);
String curTime = String.valueOf((new Date()).getTime() / 1000L);
/*
* 参考计算CheckSum的java代码,在上述文档的参数列表中,有CheckSum的计算文档示例
*/
String checkSum = CheckSumBuilder.getCheckSum(APP_SECRET, NONCE, curTime);
// 设置请求的header
httpPost.addHeader("AppKey", APP_KEY);
httpPost.addHeader("Nonce", NONCE);
httpPost.addHeader("CurTime", curTime);
httpPost.addHeader("CheckSum", checkSum);
httpPost.addHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
// 设置请求的的参数,requestBody参数
List<NameValuePair> nvps = new ArrayList<NameValuePair>();
/*
* 1.如果是模板短信,请注意参数mobile是有s的,详细参数配置请参考“发送模板短信文档”
* 2.参数格式是jsonArray的格式,例如 "['13888888888','13666666666']"
* 3.params是根据你模板里面有几个参数,那里面的参数也是jsonArray格式
*/
nvps.add(new BasicNameValuePair("templateid", TEMPLATEID));
nvps.add(new BasicNameValuePair("mobile", MOBILE));
nvps.add(new BasicNameValuePair("codeLen", CODELEN));
httpPost.setEntity(new UrlEncodedFormEntity(nvps, "utf-8"));
// 执行请求
HttpResponse response = httpClient.execute(httpPost);
/*
* 1.打印执行结果,打印结果一般会200、315、403、404、413、414、500
* 2.具体的code有问题的可以参考官网的Code状态表
*/
System.out.println(EntityUtils.toString(response.getEntity(), "utf-8"));
}
}
需要导入jar包httpcore-4.4.3.jar和httpclient-4.5.1.jar
如果需要了解的更详细的可以去官方文档中查阅