springboot实现国内发送短信验证码功能

1、引入依赖

<!--短信jar包-->
        <dependency>
            <groupId>commons-httpclient</groupId>
            <artifactId>commons-httpclient</artifactId>
            <version>3.1</version>
        </dependency>
<!--SpringBoot Boot Redis-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <!-- 提供Redis连接池 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
        </dependency>

2、验证码过期时间可以使用reids实现,配置redis

spring:
  # redis配置 
  redis:
    # Redis数据库索引(默认为0)
    database: 0
    # Redis服务器地址
    host: localhost
    # Redis服务器连接端口
    port: 6379
    # Redis服务器连接密码(默认为空)
    password: 123456
    # 连接超时时间
    timeout: 10s
    lettuce:
      pool:
        # 连接池最大连接数
        max-active: 200
        # 连接池最大阻塞等待时间(使用负值表示没有限制)
        max-wait: -1ms
        # 连接池中的最大空闲连接
        max-idle: 10
        # 连接池中的最小空闲连接
        min-idle: 0

3、编写reids工具类

package com.cloud.web.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.data.redis.core.BoundSetOperations;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * spring redis 工具类
 * 
 * @author hdb
 **/
@Component
public class RedisUtil
{
    @Autowired
    public RedisTemplate redisTemplate;


    public String doString(final byte[] bytes){
        return (String) redisTemplate
                .getValueSerializer()
                .deserialize(bytes);
    }


    public  void release(final String key,final Object value){
        redisTemplate.convertAndSend(key,value);
    }

    /**
     * 缓存基本的对象,Integer、String、实体类等
     *
     * @param key 缓存的键值
     * @param value 缓存的值
     */
    public <T> void setCacheObject(final String key, final T value)
    {
        redisTemplate.opsForValue().set(key, value);
    }

    public void expire(final String key, final long timeout)
    {
        expire(key,null,timeout,TimeUnit.DAYS);
    }

    /**
     * 设置有效时间
     *
     * @param key Redis键
     * @param timeout 超时时间
     */
    public <T> void expire(final String key,final T value, final long timeout)
    {
        expire(key,value,timeout,TimeUnit.DAYS);
    }

    /**
     * 设置有效时间
     *
     * @param key Redis键
     * @param value 值
     * @param timeout 超时时间
     * @param unit 时间单位
     */
    public <T> void expire(final String key,final T value, final long timeout, final TimeUnit unit)
    {
        redisTemplate.opsForValue().set(key,value,timeout,unit);
    }

    /**
     * 判断 key是否存在
     *
     * @param key 键
     * @return true 存在 false不存在
     */
    public Boolean hasKey(String key)
    {
        return redisTemplate.hasKey(key);
    }

    /**
     * 获得缓存的基本对象。
     *
     * @param key 缓存键值
     * @return 缓存键值对应的数据
     */
    public <T> T getCacheObject(final String key)
    {
        ValueOperations<String, T> operation = redisTemplate.opsForValue();
        return operation.get(key);
    }

    /**
     * 删除单个对象
     *
     * @param key
     */
    public boolean deleteObject(final String key)
    {
        return redisTemplate.delete(key);
    }

    /**
     * 删除集合对象
     *
     * @param collection 多个对象
     * @return
     */
    public long deleteObject(final Collection collection)
    {
        return redisTemplate.delete(collection);
    }

    /**
     * 缓存List数据
     *
     * @param key 缓存的键值
     * @param dataList 待缓存的List数据
     * @return 缓存的对象
     */
    public <T> long setCacheList(final String key, final List<T> dataList)
    {
        Long count = redisTemplate.opsForList().rightPushAll(key, dataList);
        return count == null ? 0 : count;
    }

    /**
     * 获得缓存的list对象
     *
     * @param key 缓存的键值
     * @return 缓存键值对应的数据
     */
    public <T> List<T> getCacheList(final String key)
    {
        return redisTemplate.opsForList().range(key, 0, -1);
    }

    /**
     * 缓存Set
     *
     * @param key 缓存键值
     * @param dataSet 缓存的数据
     * @return 缓存数据的对象
     */
    public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet)
    {
        BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);
        Iterator<T> it = dataSet.iterator();
        while (it.hasNext())
        {
            setOperation.add(it.next());
        }
        return setOperation;
    }

    /**
     * 获得缓存的set
     *
     * @param key
     * @return
     */
    public <T> Set<T> getCacheSet(final String key)
    {
        return redisTemplate.opsForSet().members(key);
    }

    /**
     * 缓存ZSet
     *
     * @param key 缓存键值
     * @return 缓存数据的对象
     */
    public <T> boolean setCacheZSet(final String key,
                                    final Double score,
                                    final T member,
                                    final Long timeout,
                                    final TimeUnit timeUnit) {
        redisTemplate.opsForZSet().add(key, member, score);
        Boolean expire = redisTemplate.expire(key, timeout, timeUnit);

        return expire;
    }

    /**
     * 根据Score值查询集合元素, 从大到小排序
     *
     * @param key
     * @param min
     * @param max
     * @return
     */
    public <T> Set<T> getCacheZSet(String key, double min, double max) {
        return redisTemplate.opsForZSet().reverseRangeByScoreWithScores(key, min, max);
    }
    /**
     * 缓存Map
     *
     * @param key
     * @param dataMap
     */
    public <T> void setCacheMap(final String key, final Map<String, T> dataMap)
    {
        if (dataMap != null) {
            redisTemplate.opsForHash().putAll(key, dataMap);
        }
    }

    /**
     * 获得缓存的Map
     *
     * @param key
     * @return
     */
    public <T> Map<String, T> getCacheMap(final String key)
    {
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * 往Hash中存入数据
     *
     * @param key Redis键
     * @param hKey Hash键
     * @param value 值
     */
    public <T> void setCacheMapValue(final String key, final String hKey, final T value)
    {
        redisTemplate.opsForHash().put(key, hKey, value);
    }

    /**
     * 获取Hash中的数据
     *
     * @param key Redis键
     * @param hKey Hash键
     * @return Hash中的对象
     */
    public <T> T getCacheMapValue(final String key, final String hKey)
    {
        HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();
        return opsForHash.get(key, hKey);
    }

    public void deleteCacheMapValue(final String key, final String... hKey)
    {
        redisTemplate.opsForHash().delete(key, (Object) hKey);
    }

    /**
     * 获取多个Hash中的数据
     *
     * @param key Redis键
     * @param hKeys Hash键集合
     * @return Hash对象集合
     */
    public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys)
    {
        return redisTemplate.opsForHash().multiGet(key, hKeys);
    }

    /**
     * 获得缓存的基本对象列表
     * 
     * @param pattern 字符串前缀
     * @return 对象列表
     */
    public Collection<String> keys(final String pattern)
    {
        return redisTemplate.keys(pattern);
    }
}

4、注册一个网建短信平台的账号

网建地址:https://www.smschinese.com.cn/

注册成功之后,进入首页会赠送5条免费的短信余额,意思就是你只能发5条短信,用完之后就需要自己充值,我这边是已经用了2条之后的了

5、获取用户名和短信密钥、编辑签名

6、编写发送短信工具类

package com.cloud.system.utils;


import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.methods.PostMethod;

import java.io.IOException;

public class SendMsgUtils {
    private static final String UID = "xue17620789807";//这是建网SMS 上的登陆账号
    private static final String KEY = "078A77702A25DC2EE728A00C544C5427"; //这是密钥

    /**
     * 手机发送短信
     * @param phone  手机号码
     * @param context  发送短信内容
     */
    public static String send(String phone, String context) {

        PostMethod post = null;
        try {
            //创建Http客户端
            HttpClient client = new HttpClient();
            //创建一个post方法
            post = new PostMethod("http://utf8.api.smschinese.cn");
            //添加请求头信息
            post.addRequestHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf8");//在头文件中设置转码

            NameValuePair[] data = {new NameValuePair("Uid", UID),
                    new NameValuePair("Key", KEY),
                    new NameValuePair("smsMob", phone),
                    new NameValuePair("smsText", context)};
            //设置请求体
            post.setRequestBody(data);
            //执行post方法
            client.executeMethod(post);

            //获取响应头信息
            Header[] headers = post.getResponseHeaders();
            //获取状态码
            int statusCode = post.getStatusCode();
            System.out.println("statusCode:" + statusCode);
            //循环打印头信息,暂时不需要
            /*for (Header h : headers) {
                System.out.println(h.toString());
            }*/
            //获取相应体
            return new String(post.getResponseBodyAsString().getBytes("utf8"));

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (post != null) {
                //关闭资源
                post.releaseConnection();
            }
        }
        return null;
    }

}

5、单元测试

package com.cloud.system;

import com.cloud.system.constant.CustomException;
import com.cloud.system.constant.VerificationConstant;
import com.cloud.system.utils.SendMsgUtils;
import com.cloud.system.utils.StrUtils;
import com.cloud.web.utils.LogUtil;
import com.cloud.web.utils.RedisUtil;
import org.apache.commons.lang3.StringUtils;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

@SpringBootTest
public class AppTest {

    @Resource
    private RedisUtil redisUtil;

    @Test
    void test(){
        String context=null;
        try {
            context = sendRegisterVerificationCode("11111111111");//电话号码
        }catch (CustomException e){
            System.out.println("亲!一分钟以内不能发送多次验证码!!");
        }
        String send = SendMsgUtils.send("1111111111", context);//电话号码和内容
        if(StringUtils.isNotBlank(send)){
            int count = Integer.parseInt(send);
            if(count>0){
                System.out.println("发送成功");
            }
            System.out.println(outputLog(count));
        }
    }

    private String outputLog(int count){
        switch (count){
            case -1:
                LogUtil.error("没有该用户账户");
                break;
            case -2:
                LogUtil.error("接口密钥不正确");
                break;
            case -21:
                LogUtil.error("MD5接口密钥加密不正确");
                break;
            case -3:
                LogUtil.error("短信数量不足");
                break;
            case -11:
                LogUtil.error("该用户被禁用");
                break;
            case -14:
                LogUtil.error("短信内容出现非法字符");
                break;
            case -4:
                LogUtil.error("手机号格式不正确");
                return "手机号格式不正确";
            case -41:
                LogUtil.error("手机号码为空");
                return "手机号码为空";
            case -42:
                LogUtil.error("短信内容为空");
                break;
            case -51:
                LogUtil.error("短信签名格式不正确");
                break;
            case -52:
                LogUtil.error("短信签名太长");
                break;
            case -6:
                LogUtil.error("IP限制");
                break;
            default:
                LogUtil.error("未知异常");
                break;
        }
        return "发送失败";
    }

    private String sendRegisterVerificationCode(String phone) throws CustomException {
        //随机产生4个字符
        String value = StrUtils.getComplexRandomString(4);
        //在redis中通过key获取对应的值        value:时间戳
        String valueCode = (String) redisUtil.getCacheObject(phone + ":" + VerificationConstant.USER_REG);
        //如果不为空,就意味着验证码没有过期,依然是在5分钟以内
        if(org.apache.commons.lang3.StringUtils.isNotEmpty(valueCode)){
            //开始时间戳
            String beginTimer = valueCode.split(":")[1];

            if(System.currentTimeMillis()-Long.valueOf(beginTimer)<=60*1000){
                //自定义异常,自己创建一个就可以了
                throw new CustomException("亲!一分钟以内不能发送多次验证码!!");
            }
            //重新发送之后,删除原有的验证码,采用新的
            redisUtil.deleteObject(phone + ":" + VerificationConstant.USER_REG);
        }
        //存储redis中,设置有效期是5分钟
        redisUtil.expire(phone + ":" + VerificationConstant.USER_REG,
                value + ":" + System.currentTimeMillis(), 5, TimeUnit.MINUTES);
        //发送手机验证码
        return  "尊敬的用户,您的验证码为:" + value + ", 请您在5分钟以内完成注册!!";
    }
}

短信发送后返回值说明
-1 没有该用户账户
-2 接口密钥不正确
不是账户登陆密码
-21 MD5接口密钥加密不正确
-3 短信数量不足
-11 该用户被禁用
-14 短信内容出现非法字符
-4 手机号格式不正确
-41 手机号码为空
-42 短信内容为空
-51 短信签名格式不正确
-52 短信签名太长
建议签名10个字符以内
-6 IP限制
大于0 短信发送数量
 

  • 16
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值