一砂短信发送(免费试用)实现验证码登录
一砂短信发送(免费试用)
昨日在阿里云市场发现一个很便宜的短信发送平台,现记录食用方式如下
1. 云市场链接
【国际短信/国内短信】海外短信接口 国外短信 国内短信 短信通知 短信验证 营销短信 短信群发 (免费试用)
购买短信包后可以在阿里云控制台->云市场->已购买的服务看到如下信息
2. 需要的依赖
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
3. 创建工具类
import com.alibaba.fastjson.JSONObject;
import org.apache.http.HttpResponse;
import org.apache.http.util.EntityUtils;
import java.util.HashMap;
import java.util.Map;
/**
* @author 枫叶
* @version 1.0
* @date 2020/12/22
*/
public class SmsUtil {
public static int send(String mobile) {
String host = "https://intlsms.market.alicloudapi.com";
String path = "/comms/sms/sendmsgall";
String method = "POST";
String appcode = "第一步中获取的AppCode";
int mobileCode = (int) ((Math.random() * 9 + 1) * 100000);
Map<String, String> headers = new HashMap<String, String>();
//最后在header中的格式(中间是英文空格)为Authorization:APPCODE
headers.put("Authorization", "APPCODE " + appcode);
//根据API的要求,定义相对应的Content-Type
headers.put("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
Map<String, String> querys = new HashMap<>();
Map<String, String> bodys = new HashMap<>();
//发送结果回执地址(可选)
// bodys.put("callbackUrl", "http://test.dev.esandcloud.com");
bodys.put("channel", "0");
bodys.put("mobile", "+86" + mobile);
//模板id,这里使用默认模板
bodys.put("templateID", "0000000");
//消息内容,后面的5代表验证码过期时间
bodys.put("templateParamSet", mobileCode + ", 5");
try {
/**
* 重要提示如下:
* HttpUtils请从
* https://github.com/aliyun/api-gateway-demo-sign-java/blob/master/src/main/java/com/aliyun/api/gateway/demo/util/HttpUtils.java
* 下载
*
* 相应的依赖请参照
* https://github.com/aliyun/api-gateway-demo-sign-java/blob/master/pom.xml
*/
HttpResponse response = HttpUtils.doPost(host, path, method, headers, querys, bodys);
//获取response的body
JSONObject resp = JSONObject.parseObject(EntityUtils.toString(response.getEntity()));
/**
正确的返回如下
{
"code":"0000",
"msg":"成功。",
"externalId":"f9a57d7e9c3a11ea801e02429b292a1b",
"requestId":"20200522224610325cxnpasmd"
}
*/
if ("0000".equals(resp.getString("code"))) {
return mobileCode;
} else {
System.out.println("错误!");
System.out.println(resp);
}
} catch (Exception e) {
e.printStackTrace();
}
return -1;
}
}
headers参数如下 api参考
HttpUtils如下
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @author 枫叶
* @version 1.0
* @date 2020/12/22
*/
public class HttpUtils {
/**
* get
*
* @param host
* @param path
* @param method
* @param headers
* @param querys
* @return
* @throws Exception
*/
public static HttpResponse doGet(String host, String path, String method,
Map<String, String> headers,
Map<String, String> querys)
throws Exception {
HttpClient httpClient = wrapClient(host);
HttpGet request = new HttpGet(buildUrl(host, path, querys));
for (Map.Entry<String, String> e : headers.entrySet()) {
request.addHeader(e.getKey(), e.getValue());
}
return httpClient.execute(request);
}
/**
* post form
*
* @param host
* @param path
* @param method
* @param headers
* @param querys
* @param bodys
* @return
* @throws Exception
*/
public static HttpResponse doPost(String host, String path, String method,
Map<String, String> headers,
Map<String, String> querys,
Map<String, String> bodys)
throws Exception {
HttpClient httpClient = wrapClient(host);
HttpPost request = new HttpPost(buildUrl(host, path, querys));
for (Map.Entry<String, String> e : headers.entrySet()) {
request.addHeader(e.getKey(), e.getValue());
}
if (bodys != null) {
List<NameValuePair> nameValuePairList = new ArrayList<NameValuePair>();
for (String key : bodys.keySet()) {
nameValuePairList.add(new BasicNameValuePair(key, bodys.get(key)));
}
UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(nameValuePairList, "utf-8");
formEntity.setContentType("application/x-www-form-urlencoded; charset=UTF-8");
request.setEntity(formEntity);
}
return httpClient.execute(request);
}
/**
* Post String
*
* @param host
* @param path
* @param method
* @param headers
* @param querys
* @param body
* @return
* @throws Exception
*/
public static HttpResponse doPost(String host, String path, String method,
Map<String, String> headers,
Map<String, String> querys,
String body)
throws Exception {
HttpClient httpClient = wrapClient(host);
HttpPost request = new HttpPost(buildUrl(host, path, querys));
for (Map.Entry<String, String> e : headers.entrySet()) {
request.addHeader(e.getKey(), e.getValue());
}
if (StringUtils.isNotBlank(body)) {
request.setEntity(new StringEntity(body, "utf-8"));
}
return httpClient.execute(request);
}
/**
* Post stream
*
* @param host
* @param path
* @param method
* @param headers
* @param querys
* @param body
* @return
* @throws Exception
*/
public static HttpResponse doPost(String host, String path, String method,
Map<String, String> headers,
Map<String, String> querys,
byte[] body)
throws Exception {
HttpClient httpClient = wrapClient(host);
HttpPost request = new HttpPost(buildUrl(host, path, querys));
for (Map.Entry<String, String> e : headers.entrySet()) {
request.addHeader(e.getKey(), e.getValue());
}
if (body != null) {
request.setEntity(new ByteArrayEntity(body));
}
return httpClient.execute(request);
}
/**
* Put String
* @param host
* @param path
* @param method
* @param headers
* @param querys
* @param body
* @return
* @throws Exception
*/
public static HttpResponse doPut(String host, String path, String method,
Map<String, String> headers,
Map<String, String> querys,
String body)
throws Exception {
HttpClient httpClient = wrapClient(host);
HttpPut request = new HttpPut(buildUrl(host, path, querys));
for (Map.Entry<String, String> e : headers.entrySet()) {
request.addHeader(e.getKey(), e.getValue());
}
if (StringUtils.isNotBlank(body)) {
request.setEntity(new StringEntity(body, "utf-8"));
}
return httpClient.execute(request);
}
/**
* Put stream
* @param host
* @param path
* @param method
* @param headers
* @param querys
* @param body
* @return
* @throws Exception
*/
public static HttpResponse doPut(String host, String path, String method,
Map<String, String> headers,
Map<String, String> querys,
byte[] body)
throws Exception {
HttpClient httpClient = wrapClient(host);
HttpPut request = new HttpPut(buildUrl(host, path, querys));
for (Map.Entry<String, String> e : headers.entrySet()) {
request.addHeader(e.getKey(), e.getValue());
}
if (body != null) {
request.setEntity(new ByteArrayEntity(body));
}
return httpClient.execute(request);
}
/**
* Delete
*
* @param host
* @param path
* @param method
* @param headers
* @param querys
* @return
* @throws Exception
*/
public static HttpResponse doDelete(String host, String path, String method,
Map<String, String> headers,
Map<String, String> querys)
throws Exception {
HttpClient httpClient = wrapClient(host);
HttpDelete request = new HttpDelete(buildUrl(host, path, querys));
for (Map.Entry<String, String> e : headers.entrySet()) {
request.addHeader(e.getKey(), e.getValue());
}
return httpClient.execute(request);
}
private static String buildUrl(String host, String path, Map<String, String> querys) throws UnsupportedEncodingException {
StringBuilder sbUrl = new StringBuilder();
sbUrl.append(host);
if (!StringUtils.isBlank(path)) {
sbUrl.append(path);
}
if (null != querys) {
StringBuilder sbQuery = new StringBuilder();
for (Map.Entry<String, String> query : querys.entrySet()) {
if (0 < sbQuery.length()) {
sbQuery.append("&");
}
if (StringUtils.isBlank(query.getKey()) && !StringUtils.isBlank(query.getValue())) {
sbQuery.append(query.getValue());
}
if (!StringUtils.isBlank(query.getKey())) {
sbQuery.append(query.getKey());
if (!StringUtils.isBlank(query.getValue())) {
sbQuery.append("=");
sbQuery.append(URLEncoder.encode(query.getValue(), "utf-8"));
}
}
}
if (0 < sbQuery.length()) {
sbUrl.append("?").append(sbQuery);
}
}
return sbUrl.toString();
}
private static HttpClient wrapClient(String host) {
HttpClient httpClient = new DefaultHttpClient();
if (host.startsWith("https://")) {
sslClient(httpClient);
}
return httpClient;
}
private static void sslClient(HttpClient httpClient) {
try {
SSLContext ctx = SSLContext.getInstance("TLS");
X509TrustManager tm = new X509TrustManager() {
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
@Override
public void checkClientTrusted(X509Certificate[] xcs, String str) {
}
@Override
public void checkServerTrusted(X509Certificate[] xcs, String str) {
}
};
ctx.init(null, new TrustManager[] { tm }, null);
SSLSocketFactory ssf = new SSLSocketFactory(ctx);
ssf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
ClientConnectionManager ccm = httpClient.getConnectionManager();
SchemeRegistry registry = ccm.getSchemeRegistry();
registry.register(new Scheme("https", 443, ssf));
} catch (KeyManagementException ex) {
throw new RuntimeException(ex);
} catch (NoSuchAlgorithmException ex) {
throw new RuntimeException(ex);
}
}
}
4. 创建web接口实现验证码发送
这里使用了session,在前后端分离中应使用中心缓存技术或数据库
/**
* @author 枫叶
* @version 1.0
* @date 2020/12/22
*/
@WebServlet(value = "/sms/send", name = "sms")
public class SmsServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
BufferedReader reader = req.getReader();
JSONObject mobileJson = JSONObject.parseObject(reader.readLine());
String mobile = mobileJson.getString("mobile");
//这里应该查询数据库获取手机号对应的用户,若没有用户则创建新用户,并绑定手机号
JSONObject token = (JSONObject) req.getSession().getAttribute(mobile);
if (token != null) {
long curren = System.currentTimeMillis();
Long timeOut = token.getLong("timeOut");
if (curren - timeOut > 60 * 1000) {
//重复请求验证码
resp.getWriter().println(DataResponse.error("操作过于频繁请1分钟后再试。"));
return;
}
}
int mobileCode = SmsUtil.send(mobile);
if (mobileCode > 0) {
//发送验证码成功
token = new JSONObject();
token.put("mobileCode", mobileCode);
token.put("timeOut", System.currentTimeMillis());
req.getSession().setAttribute(mobile, token);
resp.getWriter().println(DataResponse.ok());
return;
}
resp.getWriter().println(DataResponse.error());
}
}
5. 创建web接口实现短信验证码登录
这里使用了session,在前后端分离中应使用中心缓存技术或数据库
/**
* @author 枫叶
* @version 1.0
* @date 2020/12/22
*/
@WebServlet(value = "/sms/login", name = "login")
public class LoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
JSONObject json = JSONObject.parseObject(req.getReader().readLine());
String mobile = json.getString("mobile");
Integer mobileCode = json.getInteger("mobileCode");
JSONObject token = (JSONObject) req.getSession().getAttribute(mobile);
System.out.println("token: "+token);
//未获取验证码
if (token == null) {
resp.getWriter().println(DataResponse.error(400, "请先获取验证码。"));
return;
}
long timeOut = System.currentTimeMillis();
//已有验证码
if (timeOut - token.getLong("timeOut") > (5 * 60 * 1000)) {
//验证码过期
resp.getWriter().println(DataResponse.error(420, "验证码过期,请重新获取。"));
req.getSession().removeAttribute(mobile);
return;
}
if (token.getInteger("mobileCode").equals(mobileCode)) {
//验证码正确
req.getSession().setAttribute("yongHu", mobile);
resp.getWriter().println(DataResponse.ok("登录成功!").put("data: ",mobile));
req.getSession().removeAttribute(mobile);
} else {
//验证码错误。
resp.getWriter().println(DataResponse.error("验证码错误。"));
}
}
}
6. 上面用到的返回封装类DataResponse如下
public class DataResponse{
private final Map<String,Object> result;
@Override
public String toString() {
//这里也用到了fastJSON并且设置了日期的格式化格式
JSON.DEFFAULT_DATE_FORMAT = "yyyy-MM-dd hh:mm:ss";
return JSON.toJSONString(result,SerializerFeature.WriteDateUseDateFormat);
}
public DataResponse(Integer code,String msg,Object data){
result = new HashMap<>();
result.put("code",code);
result.put("msg",msg);
result.put("data",data);
}
public DataResponse(Integer code,String msg){
this(code,msg,null);
}
public DataResponse() {
result=new HashMap<>();
}
public DataResponse put(String key,Object value){
result.put(key,value);
return this;
}
public static DataResponse ok(){
return new DataResponse(200,"成功!");
}
public static DataResponse ok(String msg){
return new DataResponse(200,msg);
}
public static DataResponse ok(int code,String msg){
return new DataResponse(code,msg);
}
public static DataResponse ok(String msg,Object data){
return new DataResponse(200,msg,data);
}
public static DataResponse ok(int code,String msg,Object data){
return new DataResponse(200,msg,data);
}
public static DataResponse error(){
return new DataResponse(500,"服务器错误,操作失败!");
}
public static DataResponse error(String msg){
return new DataResponse(500,msg);
}
public static DataResponse error(int code,String msg){
return new DataResponse(code,msg);
}
public Object get(String key){
return result.get(key);
}
public Object getData(){
return result.get("data");
}
public void setCode(int code) {
result.put("code",code);
}
public void setMsg(String msg) {
result.put("msg",msg);
}
public void setData(Object data) {
result.put("data",data);
}
}
7. 测试
前端关键js代码如下
//状态机是否已经发送验证码
var mobileFlag = false;
//重新发送验证码的间隔时间(秒)
var reSend = 60;
var intervalID;
$("#mobileSend").click(function () {
let reg = /^1([3,4,5,6,7,8,9])\d{9}$/;
let mobile = $("#mobile").val();
if (!(reg.test(mobile))) {
alert("手机号码有误,请重填");
//将光标移到手机号输入框中
document.getElementById("mobile").focus();
return false;
}
$.ajax({
url: "/sms/send",
type: "post",
dataType: "json",
contentType: "application/json; charset=UTF-8",
data: JSON.stringify({
mobile: mobile
}),
success: function (resp) {
if (resp.code === 200) {
mobileFlag = true;
$("#mobileSend").attr("disabled", true)
//页面显示倒计时
intervalID = setInterval(function () {
let mobileSend = $("#mobileSend");
mobileSend.val(reSend-- + "秒后可重新发送");
if (reSend < 0) {
clearInterval(intervalID);
mobileSend.removeAttr("disabled");
reSend = 60;
mobileSend.val("重新发送");
}
}, 1000);
} else {
alert("发送验证码失败,请稍后再试!");
}
},
error: function (xhr) {
alert("错误:" + xhr.msg);
}
})
})
$("#mobileLogin").click(function () {
if (!mobileFlag) {
alert("请先获取验证码。")
return;
}
let mobile = $("#mobile").val();
let mobileCode = $("#mobileCode").val();
$.ajax({
url: "/sms/login",
dataType: "json",
type: "post",
contentType: "application/json; charset=UTF-8",
data: JSON.stringify({
mobile: mobile,
mobileCode: mobileCode
}),
success: function (data) {
console.log(data)
if (data.code === 200) {
alert("登录成功");
//跳转页面
window.location = "/";
} else {
alert("登录失败 " + data.msg);
}
},
error: function (xhr) {
alert("服务器异常,请稍后再试!");
}
})
})