我用的短信平台是阿里云的,需要付费购买服务,购买地址:https://common-buy.aliyun.com/?spm=5176.8195934.907839.sms6.312c4183mzE9Yb&&commodityCode=newdysmsbag#/buy
付费完成后,首先申请短信签名和短信模板:https://help.aliyun.com/document_detail/55327.html?spm=a2c4g.11186623.6.549.huzd56。
短信签名:根据用户属性来创建符合自身属性的签名信息。企业用户需要上传相关企业资质证明,个人用户需要上传证明个人身份的证明。注意:短信签名需要审核通过后才可以使用。
短信模板:短信模板,即具体发送的短信内容。短信模板可以支持验证码、短信通知、推广短信、国际/港澳台消息四种模式。验证码和短信通知,通过变量替换实现个性短信定制。推广短信不支持在模板中添加变量。短信模板需要审核通过后才可以使用。
短信示例:【阿里云】 验证码${number},您正进行支付宝的身份验证,打死不告诉别人!这里的短信签名:阿里云,短信模板: 验证码${number},您正进行支付宝的身份验证,打死不告诉别人!
最后获取 asscessKeyId 和 accessKeySecret 。结合阿里云提供的开发者文档即可进行接口开发,短信开发api文档:https://help.aliyun.com/product/44282.html?spm=a2c4g.750001.6.1.T84wBi
一、安装redis
下载地址:http://redis.io/download,下载最新稳定版本。
在/usr/local创建redis文件夹上传下载的安装包到redis目录下:
安装:
$ tar -zxvf redis-4.0.9.tar.gz
$ cd redis-4.0.9
$ make
安装完成后的目录:
$ cd src
$ ./redis-server ../redis.conf
./redis-server 这种方式启动redis 使用的是默认配置。可以通过启动参数告诉redis使用指定配置文件使用下面命令启动。
二、创建maven工程,目录结构如下:
三、pom.xml文件添加redis依赖
org.springframework.boot
spring-boot-starter-data-redis
四、application.properties添加redis配置
五、随机生成验证码工具类
public class IdentifyCodeUtil {
public static String getRandom() {
String num = "";
for (int i = 0; i < 6; i++) {
num = num + String.valueOf((int) Math.floor(Math.random() * 9 + 1));
}
return num;
}
}
六、redis缓存配置类
sprringboot启动类Application.java加入注解:@EnableCaching
配置redis采用缓存,设置key和value的序列化方式
package com.jp.tech.applet.ms.sms;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
/**
* Redis缓存配置类
*
* @author yangfeng
*/
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {
//缓存管理器
@Bean
public CacheManager cacheManager(@SuppressWarnings("rawtypes") RedisTemplate redisTemplate) {
RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
//设置缓存过期时间
//cacheManager.setDefaultExpiration(20);
return cacheManager;
}
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory factory) {
StringRedisTemplate template = new StringRedisTemplate(factory);
setSerializer(template);//设置序列化工具
template.afterPropertiesSet();
return template;
}
private void setSerializer(StringRedisTemplate template) {
@SuppressWarnings({"rawtypes", "unchecked"})
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
template.setValueSerializer(jackson2JsonRedisSerializer);
}
}
七、redis接口类
此接口用于连接redis操作生成的验证码,设置过期时间。
public interface IRedisService {
/**
* 设置key-value
* @param key
* @param value
*/
void setKey(String key, String value);
/**
* 获取key
* @param key
* @return
*/
String getValue(String key);
/**
* 删除key
* @param key
*/
void delete(String key);
}
八、redis接口实现类
保存、获取、删除验证码接口实现方法。
import com.jp.tech.applet.ms.sms.service.IRedisService;
import org.springframework.data.redis.core.*;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@Service
public class RedisService implements IRedisService {
@Resource
private RedisTemplate redisTemplate;
@Override
public void setKey(String key, String value) {
ValueOperations ops = redisTemplate.opsForValue();
ops.set(key, value, 900, TimeUnit.SECONDS);//15分钟过期
}
@Override
public String getValue(String key) {
ValueOperations ops = redisTemplate.opsForValue();
return ops.get(key);
}
@Override
public void delete(String key) {
redisTemplate.delete(key);
}
}
九、发送短信接口类
package com.jp.zpzc.service;
import com.aliyuncs.dysmsapi.model.v20170525.QuerySendDetailsResponse;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.aliyuncs.exceptions.ClientException;
import com.jp.framework.common.model.ServiceResult;
public interface ISmsService {
/**
* 发送短信接口
*
* @param phoneNums 手机号码
* @param signName 模板签名
* @param templeteCode 模板代码
* @param templateParam 模板替换参数
* @param outId 提供给业务方扩展字段
* @return
* @throws ClientException
*/
SendSmsResponse sendSms(String phoneNums, String signName, String templeteCode,
String templateParam, String outId) throws ClientException;
/**
* 查询短信发送明细
*
* @param phoneNumber
* @param bizId 业务流水号
* @return
* @throws ClientException
*/
QuerySendDetailsResponse querySendDetails(String phoneNumber, String bizId) throws ClientException;
/**
* 发送短信服务
*
* @param mobile 手机号
* @return
*/
ServiceResult
十、短信接口实现类
pom中引入阿里云短信SDK:
com.aliyun
aliyun-java-sdk-core
3.3.1
com.aliyun
aliyun-java-sdk-dysmsapi
1.1.0
application.properties加入调用接口的认证信息:
aliyun.sms.accessKeyId=LTAIr0fPaMi66tCy111
aliyun.sms.accessKeySecret=bQ9Re8uV14yRKKhWdWAbbUO15EM7w1111
这里我用的是阿里云短信平台,根据提供的demo自己整理后的接口实现。
smsService.sendSms(phoneName, "XXXXXX", "XXXXXXX", JSON.toJSONString(codeMap), null)
第一个XXXXX代表申请的短信签名名称,第二个代表申请的短信模板编码,改成自己申请的即可。
package com.jp.zpzc.service.impl;
import com.alibaba.fastjson.JSON;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.dysmsapi.model.v20170525.QuerySendDetailsRequest;
import com.aliyuncs.dysmsapi.model.v20170525.QuerySendDetailsResponse;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import com.jp.framework.common.model.ServiceResult;
import com.jp.framework.common.model.ServiceResultHelper;
import com.jp.framework.common.util.Constant;
import com.jp.framework.common.util.IdentifyCodeUtil;
import com.jp.zpzc.service.IRedisService;
import com.jp.zpzc.service.ISmsService;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@Service
public class SmsService implements ISmsService {
private Logger logger = LoggerFactory.getLogger(this.getClass());
//产品名称:云通信短信API产品,开发者无需替换
static final String product = "Dysmsapi";
//产品域名,开发者无需替换
static final String domain = "dysmsapi.aliyuncs.com";
@Value("${aliyun.sms.accessKeyId}")
private String accessKeyId;
@Value("${aliyun.sms.accessKeySecret}")
private String accessKeySecret;
@Resource
private IRedisService redisService;
/**
* 发送短信服务
*
* @param mobile
* @return
*/
public ServiceResult
十一、controller类
调用redis存入随机生成的验证码,调用短信平台接口发送验证码。
package com.jp.zpzc.controller.sms;
import com.jp.zpzc.service.ISmsService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* 短信验证码接口
*
* @author yangfeng
* @date 2018-06-09 16:02
**/
@RestController
@RequestMapping("/smsVerifityCode")
public class SmsVerifityCodeController {
private Logger LOG = LoggerFactory.getLogger(this.getClass());
@Resource
private ISmsService smsService;
/**
* 发送短信验证码
*
* @param mobile
* @return
*/
@RequestMapping("/sendMessage")
public Object sendMessage(@RequestParam String mobile) {
return smsService.sendMessage(mobile);
}
/**
* 判断验证码是否正确
*
* @param mobile
* @param identifyCode
* @return
*/
@RequestMapping("/checkIsCorrectCode")
public Object checkIsCorrectCode(@RequestParam String mobile, @RequestParam String identifyCode) {
return smsService.checkIsCorrectCode(mobile, identifyCode);
}
}
十二、短信发送测试
附:
Constant类代码:
package com.jp.tech.applet.common.constant;
public class Constant {
public static class ErrorCode {
/**
* 无效参数
*/
public static Integer INVALID_PARAM_CODE = -101;
public static String INVALID_PARAM_MSG = "无效参数";
/**
* 没有权限
*/
public static Integer PERMISSION_DENIED_CODE = -102;
public static String PERMISSION_DENIED_MSG = "没有权限";
/**
* 通用错误
*/
public static Integer COMMON_ERROR_CODE = -103;
public static String COMMON_ERROR_MSG = "服务器繁忙,请稍后再试";
/**
* 登录失效
*/
public static Integer INVALID_LOGIN_CODE = -104;
public static String INVALID_LOGIN_MSG = "登录失效";
/**
* 数据库操作失败
*/
public static Integer DATABASE_OPERATION_ERROR_CODE = -105;
public static String DATABASE_OPERATION_ERROR_MSG = "数据库操作失败";
/**
* token失效
*/
public static Integer INVALID_TOKEN_CODE = -106;
public static String INVALID_TOKEN_MSG = "用户未登录或登录信息已失效";
/**
* 服务器异常
*/
public static Integer SERVER_ERROR_CODE = -200;
public static String SERVER_ERROR_MSG = "服务器异常";
}
}
ServiceResultHelper 类:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.jp.framework.common.model;
import com.jp.framework.common.exception.BaseException;
import com.jp.framework.common.util.Constant;
public class ServiceResultHelper {
public ServiceResultHelper() {
}
public static ServiceResult genResult(boolean succeed, int retCode, String msg, T obj) {
ServiceResult ret = new ServiceResult();
ret.setData(obj);
ret.setMsg(msg);
ret.setCode(retCode);
ret.setSucceed(succeed);
return ret;
}
public static boolean isSuccess(ServiceResult result) {
return result != null && result.isSucceed() && result.getCode() == 0;
}
public static T getData(ServiceResult result) {
if (result == null) {
throw new BaseException(500, "Network is error");
} else if (result.getCode() != 0) {
throw new BaseException(500, String.format("invoke hessian error! status:%s; msg:%s", result.getCode(), result.getMsg()));
} else {
return result.getData();
}
}
public static ServiceResult genResultWithSuccess(T obj) {
return genResult(true, Constant.SUCCESS, "成功", obj);
}
public static ServiceResult genResultWithSuccess() {
return genResult(true, Constant.SUCCESS, "成功", (Object)null);
}
public static ServiceResult genResultWithFaild() {
return genResult(false, Constant.FAILED, "失败", (Object)null);
}
public static ServiceResult genResultWithFaild(String msg, Integer code) {
return genResult(false, code, msg, (Object)null);
}
public static ServiceResult genResultWithDataNull() {
return genResult(false, Constant.SUCCESS, "数据为空", (Object)null);
}
public static ServiceResult toResult(BaseException baseException) {
return toResult(baseException, (Object)null);
}
public static ServiceResult toResult(BaseException baseException, T data) {
ServiceResult result = new ServiceResult();
if (baseException != null) {
result.setCode(baseException.getStatus());
result.setMsg(baseException.getMsg());
result.setSucceed(false);
}
result.setData(data);
return result;
}
}
ServiceResult 类:
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.hxkg.framework.common.util.Constant;
import com.hxkg.framework.common.dto.BaseDTO;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
@JsonInclude(Include.ALWAYS)
public final class ServiceResult extends BaseDTO {
private static final long serialVersionUID = 6977558218691386450L;
private boolean succeed = true;
private int code;
private int subCode;
private String msg;
private T data;
private Map additionalProperties;
public ServiceResult() {
this.code = Constant.SUCCESS;
this.subCode = Constant.SUCCESS;
this.additionalProperties = new HashMap();
}
public static ServiceResult error() {
return error(500, "未知异常,请联系管理员");
}
public static ServiceResult error(String msg) {
return error(500, msg);
}
public static ServiceResult error(int subCode, String msg) {
ServiceResult result = new ServiceResult();
result.setCode(Constant.FAILED);
result.setSubCode(subCode);
result.setSucceed(false);
result.setMsg(msg);
return result;
}
public static ServiceResult ok() {
return ok(Constant.SUCCESS, "成功");
}
public static ServiceResult ok(int code, String msg) {
ServiceResult result = new ServiceResult();
result.setCode(code);
result.setSucceed(true);
result.setMsg(msg);
return result;
}
public static ServiceResult ok(Object data) {
ServiceResult d = new ServiceResult();
d.setSucceed(true);
d.setData(data);
d.setCode(Constant.SUCCESS);
d.setMsg("成功");
return d;
}
public static ServiceResult ok(Object data, Map additionalProperties) {
ServiceResult d = new ServiceResult();
d.setSucceed(true);
d.setData(data);
d.setCode(Constant.SUCCESS);
d.setMsg("成功");
d.additionalProperties.putAll(additionalProperties);
return d;
}
public ServiceResult(T data) {
this.code = Constant.SUCCESS;
this.subCode = Constant.SUCCESS;
this.additionalProperties = new HashMap();
this.data = data;
}
public ServiceResult(boolean succeed, int code, String msg) {
this.code = Constant.SUCCESS;
this.subCode = Constant.SUCCESS;
this.additionalProperties = new HashMap();
this.succeed = succeed;
this.code = code;
this.msg = msg;
}
public ServiceResult(boolean succeed, T data, String msg) {
this.code = Constant.SUCCESS;
this.subCode = Constant.SUCCESS;
this.additionalProperties = new HashMap();
this.succeed = succeed;
this.data = data;
this.msg = msg;
}
public ServiceResult(boolean succeed, T data, int code, String msg) {
this.code = Constant.SUCCESS;
this.subCode = Constant.SUCCESS;
this.additionalProperties = new HashMap();
this.succeed = succeed;
this.data = data;
this.code = code;
this.msg = msg;
}
public ServiceResult(boolean succeed, String msg) {
this.code = Constant.SUCCESS;
this.subCode = Constant.SUCCESS;
this.additionalProperties = new HashMap();
this.succeed = succeed;
this.msg = msg;
}
public boolean isSucceed() {
return this.succeed;
}
public void setSucceed(boolean succeed) {
this.succeed = succeed;
}
public T getData() {
return this.data;
}
public void setData(T data) {
this.data = data;
}
public String toString() {
return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
}
public String getMsg() {
return this.msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public int getCode() {
return this.code;
}
public void setCode(int code) {
this.code = code;
}
public int getSubCode() {
return this.subCode;
}
public void setSubCode(int subCode) {
this.subCode = subCode;
}
public Map getAdditionalProperties() {
return this.additionalProperties;
}
public void setAdditionalProperties(String name, Object value) {
this.additionalProperties.put(name, value);
}
public Object getAdditionalProperties(String name) {
return this.additionalProperties.get(name);
}
}