springboot整合redis实现发送短信验证码

    我用的短信平台是阿里云的,需要付费购买服务,购买地址: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依赖

<!--redis-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

四、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<String, String> 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 RedisServiceImpl implements IRedisService {

    @Resource
    private RedisTemplate redisTemplate;

    @Override
    public void setKey(String key, String value) {
        ValueOperations<String, String> ops = redisTemplate.opsForValue();
        ops.set(key, value, 900, TimeUnit.SECONDS);//15分钟过期
    }

    @Override
    public String getValue(String key) {
        ValueOperations<String, String> ops = redisTemplate.opsForValue();
        return ops.get(key);
    }

    @Override
    public void delete(String key) {
        redisTemplate.delete(key);
    }
 
}

九、发送短信接口类

import com.aliyuncs.dysmsapi.model.v20170525.QuerySendDetailsResponse;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.aliyuncs.exceptions.ClientException;

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;
}

十、短信接口实现类

      这里我用的是阿里云短信平台,根据提供的demo自己整理后的接口实现。

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.tech.applet.ms.sms.service.ISmsService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.text.SimpleDateFormat;
import java.util.Date;

@Service
public class SmsServiceImpl 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;

    /**
     * 发送短信接口
     * @param phoneNums
     * @param signName 模板签名
     * @param templeteCode 模板代码
     * @param templateParam 模板替换参数
     * @param outId  提供给业务方扩展字段
     * @return
     * @throws ClientException
     */
    @Override
    public SendSmsResponse sendSms(String phoneNums, String signName, String templeteCode, String templateParam, String outId) throws ClientException {
        //可自助调整超时时间
        System.setProperty("sun.net.client.defaultConnectTimeout", "10000");
        System.setProperty("sun.net.client.defaultReadTimeout", "10000");

        //初始化acsClient,暂不支持region        IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", accessKeyId, accessKeySecret);
        DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", product, domain);
        IAcsClient acsClient = new DefaultAcsClient(profile);

        //组装请求对象-具体描述见控制台-文档部分内容
        SendSmsRequest request = new SendSmsRequest();
        //必填:待发送手机号
        request.setPhoneNumbers(phoneNums);
        //必填:短信签名-可在短信控制台中找到
        request.setSignName(signName);//众评众测
        //必填:短信模板-可在短信控制台中找到
        request.setTemplateCode(templeteCode);//SMS_136856023
        //可选:模板中的变量替换JSON,如模板内容为"亲爱的${name},您的验证码为${code}",此处的值为
        request.setTemplateParam(templateParam);//{"code":"152745"}

        //选填-上行短信扩展码(无特殊需求用户请忽略此字段)
        //request.setSmsUpExtendCode("90997");

        //可选:outId为提供给业务方扩展字段,最终在短信回执消息中将此值带回给调用者
        request.setOutId(outId);//zpzc

        //hint 此处可能会抛出异常,注意catch

        SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request);
        acsClient.getAcsResponse(request);
        return sendSmsResponse;
    }

    /**
     * 查询短信发送明细
     * @param phoneNumber
     * @param bizId
     * @return
     * @throws ClientException
     */
    @Override
    public QuerySendDetailsResponse querySendDetails(String phoneNumber, String bizId) throws ClientException {
        //可自助调整超时时间
        System.setProperty("sun.net.client.defaultConnectTimeout", "10000");
        System.setProperty("sun.net.client.defaultReadTimeout", "10000");

        //初始化acsClient,暂不支持region        IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", accessKeyId, accessKeySecret);
        DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", product, domain);
        IAcsClient acsClient = new DefaultAcsClient(profile);

        //组装请求对象
        QuerySendDetailsRequest request = new QuerySendDetailsRequest();
        //必填-号码
        request.setPhoneNumber(phoneNumber);
        //可选-流水号
        request.setBizId(bizId);
        //必填-发送日期 支持30天内记录查询,格式yyyyMMdd
        SimpleDateFormat ft = new SimpleDateFormat("yyyyMMdd");
        request.setSendDate(ft.format(new Date()));
        //必填-页大小
        request.setPageSize(10L);
        //必填-当前页码从1开始计数
        request.setCurrentPage(1L);

        //hint 此处可能会抛出异常,注意catch
        QuerySendDetailsResponse querySendDetailsResponse=acsClient.getAcsResponse(request);
        return querySendDetailsResponse;
    }
}

十一、controller类

       调用redis存入随机生成的验证码,调用短信平台接口发送验证码。

smsService.sendSms(phoneName, "XXXXXX", "XXXXXXX", JSON.toJSONString(codeMap), null)
第一个XXXXX代表申请的短信签名名称,第二个代表申请的短信模板编码,改成自己申请的即可。
import com.alibaba.fastjson.JSON;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.jp.tech.applet.common.constant.Constant;
import com.jp.tech.applet.common.result.ResultModel;
import com.jp.tech.applet.ms.sms.service.IRedisService;
import com.jp.tech.applet.ms.sms.service.ISmsService;
import com.jp.tech.applet.ms.sms.util.IdentifyCodeUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;


/**
 * 短信验证码接口
 *
 * @author yangfeng
 * @date 2018-06-09 16:02
 **/
@RestController
@RequestMapping("/smsVerifityCode")
public class SmsVerifityCodeController {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Resource
    private ISmsService smsService;
    @Resource
    private IRedisService redisService;

    /**
     * 发送短信验证码
     *
     * @param phoneName
     * @return
     */
    @RequestMapping("/sendMessage")
    public ResultModel sendMessage(String phoneName) {
        String identifyCode;
        //1. 判断是否缓存该账号验证码
        String returnCode = redisService.getValue(phoneName + Constant.SMS_LOGIN_IDENTIFY_CODE);
        if (!StringUtils.isEmpty(returnCode)) {
            identifyCode = returnCode;
        } else {
            identifyCode = IdentifyCodeUtil.getRandom();
            redisService.setKey(phoneName + Constant.SMS_LOGIN_IDENTIFY_CODE, identifyCode);
        }
        //2.发送短信
        Map<String, String> codeMap = new HashMap<>();
        codeMap.put("code", identifyCode);
        SendSmsResponse response;
        try {
            response = smsService.sendSms(phoneName, "XXXXXX", "XXXXXXX", JSON.toJSONString(codeMap), null);
        } catch (Exception e) {
            logger.error("sendMessage method invoke error: {}",e.getMessage());
            return ResultModel.commonError("短信发送失败:" + e.getMessage());
        }
        return ResultModel.success(response);
    }

    /**
     * 判断验证码是否正确
     *
     * @param phoneName
     * @param identifyCode
     * @return
     */
    @RequestMapping("/checkIsCorrectCode")
    public ResultModel checkIsCorrectCode(String phoneName, String identifyCode) {
        String returnCode = redisService.getValue(phoneName + Constant.SMS_LOGIN_IDENTIFY_CODE);
        if (!StringUtils.isEmpty(returnCode) && returnCode.equals(identifyCode)) {
            return ResultModel.success("验证成功");
        }
        return ResultModel.commonError("验证失败");
    }

}

十二、单元测试service接口

import com.aliyuncs.dysmsapi.model.v20170525.QuerySendDetailsResponse;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.jp.tech.applet.ms.sms.service.ISmsService;
import com.jp.tech.applet.web.Application;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import javax.annotation.Resource;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class SmsTest {
    @Resource
    private ISmsService smsService;

    @Test
    public void sendSmsTest() throws Exception {
        SendSmsResponse response = smsService.sendSms("15287XXXXX","XXX","XXXXX","{\"code\":\"152745\"}","");
        System.out.println("短信接口返回的数据----------------");
        System.out.println("Code=" + response.getCode());
        System.out.println("Message=" + response.getMessage());
        System.out.println("RequestId=" + response.getRequestId());
        System.out.println("BizId=" + response.getBizId());

        Thread.sleep(3000L);

        //查明细
        if(response.getCode() != null && response.getCode().equals("OK")) {
            QuerySendDetailsResponse querySendDetailsResponse = smsService.querySendDetails("15287XXXXX,response.getBizId());
            System.out.println("短信明细查询接口返回数据----------------");
            System.out.println("Code=" + querySendDetailsResponse.getCode());
            System.out.println("Message=" + querySendDetailsResponse.getMessage());
            int i = 0;
            for(QuerySendDetailsResponse.SmsSendDetailDTO smsSendDetailDTO : querySendDetailsResponse.getSmsSendDetailDTOs())
            {
                System.out.println("SmsSendDetailDTO["+i+"]:");
                System.out.println("Content=" + smsSendDetailDTO.getContent());
                System.out.println("ErrCode=" + smsSendDetailDTO.getErrCode());
                System.out.println("OutId=" + smsSendDetailDTO.getOutId());
                System.out.println("PhoneNum=" + smsSendDetailDTO.getPhoneNum());
                System.out.println("ReceiveDate=" + smsSendDetailDTO.getReceiveDate());
                System.out.println("SendDate=" + smsSendDetailDTO.getSendDate());
                System.out.println("SendStatus=" + smsSendDetailDTO.getSendStatus());
                System.out.println("Template=" + smsSendDetailDTO.getTemplateCode());
            }
            System.out.println("TotalCount=" + querySendDetailsResponse.getTotalCount());
            System.out.println("RequestId=" + querySendDetailsResponse.getRequestId());
        }

    }
}
单元测试通过后,短信平台整合结束。



猜你喜欢

转载自blog.csdn.net/xiaoxiangzi520/article/details/80650893