记录折腾的那点事
在折腾的道路上永不止步

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@EnableCachingpublic 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;@Servicepublic class RedisService 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);    } }

九、发送短信接口类

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<Object> sendMessage(String mobile);    /**     * 判断验证码是否正确     *     * @param mobile     * @param identifyCode     * @return     */    ServiceResult<Boolean> checkIsCorrectCode(String mobile, String identifyCode);}

十、短信接口实现类

      这里我用的是阿里云短信平台,根据提供的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;@Servicepublic 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<Object> sendMessage(String mobile) {        if (StringUtils.isEmpty(mobile)) {            return ServiceResultHelper.genResultWithFaild(Constant.ErrorCode.INVALID_PARAM_MSG, Constant.ErrorCode.INVALID_PARAM_CODE);        }        String identifyCode;        //1. 判断是否缓存该账号验证码        String returnCode = redisService.getValue(mobile + Constant.SMS_LOGIN_IDENTIFY_CODE);        if (!StringUtils.isEmpty(returnCode)) {            identifyCode = returnCode;        } else {            identifyCode = IdentifyCodeUtil.getRandom();        }        //2.发送短信        Map<String, String> codeMap = new HashMap<>();        codeMap.put("code", identifyCode);        SendSmsResponse response;        try {            response = sendSms(mobile, XXXXX, XXXXX, JSON.toJSONString(codeMap), null);            //短信发送成功后存入redis            if (response != null && Constant.SMS_SEND_STATUS_OK.equalsIgnoreCase(response.getCode()) && StringUtils.isEmpty(returnCode)) {                redisService.setKey(mobile + Constant.SMS_LOGIN_IDENTIFY_CODE, identifyCode);            }            return ServiceResultHelper.genResultWithSuccess(response);        } catch (Exception e) {            logger.error("sendMessage method invoke error: {}", e.getMessage());        }        return ServiceResultHelper.genResultWithFaild("短信发送失败", null);    }    /**     * 发送短信接口     *     * @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);        //可选:模板中的变量替换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 mobile     * @param identifyCode     * @return     */    public ServiceResult<Boolean> checkIsCorrectCode(String mobile, String identifyCode) {        if (StringUtils.isEmpty(mobile) || StringUtils.isEmpty(identifyCode)) {            return ServiceResultHelper.genResultWithFaild(Constant.ErrorCode.INVALID_PARAM_MSG, Constant.ErrorCode.INVALID_PARAM_CODE);        }        String returnCode = redisService.getValue(mobile + Constant.SMS_LOGIN_IDENTIFY_CODE);        if (!StringUtils.isEmpty(returnCode) && returnCode.equals(identifyCode)) {            return ServiceResultHelper.genResultWithSuccess();        }        return ServiceResultHelper.genResultWithFaild();    }    /**     * 查询短信发送明细     *     * @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存入随机生成的验证码,调用短信平台接口发送验证码。

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 <T> ServiceResult<T> 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 <T> boolean isSuccess(ServiceResult<T> result) {        return result != null && result.isSucceed() && result.getCode() == 0;    }    public static <T> T getData(ServiceResult<T> 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 <T> ServiceResult<T> genResultWithSuccess(T obj) {        return genResult(true, Constant.SUCCESS, "成功", obj);    }    public static <T> ServiceResult<T> genResultWithSuccess() {        return genResult(true, Constant.SUCCESS, "成功", (Object)null);    }    public static <T> ServiceResult<T> genResultWithFaild() {        return genResult(false, Constant.FAILED, "失败", (Object)null);    }    public static <T> ServiceResult<T> genResultWithFaild(String msg, Integer code) {        return genResult(false, code, msg, (Object)null);    }    public static <T> ServiceResult<T> genResultWithDataNull() {        return genResult(false, Constant.SUCCESS, "数据为空", (Object)null);    }    public static <T> ServiceResult<T> toResult(BaseException baseException) {        return toResult(baseException, (Object)null);    }    public static <T> ServiceResult<T> 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<T> 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<String, Object> additionalProperties;    public ServiceResult() {        this.code = Constant.SUCCESS;        this.subCode = Constant.SUCCESS;        this.additionalProperties = new HashMap();    }    public static ServiceResult<Boolean> error() {        return error(500, "未知异常,请联系管理员");    }    public static ServiceResult<Boolean> error(String msg) {        return error(500, msg);    }    public static ServiceResult<Boolean> 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<String, Object> 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<String, Object> 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);    }}

 

赞(0)
未经允许不得转载:ghMa » springboot整合redis实现发送短信验证码
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址