spring-boot | 集成短信验证码服务

目前,手机号对于我们的日常生活有着非常重要的作用,特别是手机号码实名认证以后,手机号如同你的身份证一样记录着我们的各种信息。所以短信验证码作为手机号的验证方式也显得尤为重要。因此,很多公司或网站用短信验证码来验证我们的身份信息。常见的使用场景有:登录注册、信息修改、异常登录、找回密码等操作。

今天给大家分享一下如何接入当前应用比较广泛的阿里云短信服务平台和容联云短信平台,其实每个短信平台接入方式都大同小异。

首先我们需要去各家短信平台申请属于我们自己的开发者账号,容联云通讯会有一定数量的免费短信条数用来测试,但是免费短信不可以自定义发送内容的模板,充值一定金额后可以申请。阿里云是后付费模式,可以使用短信自定义的短信模板或者签名。

申请好了以后我们就要这些需要的信息添加到我们的配置文件中。

pom.xml添加依赖

阿里云短信提供了通过maven方式添加依赖,容联云通讯则需要自己手动去下载jar包添加到工程目录中。

      <dependency>
            <groupId>com.aliyun.mns</groupId>
            <artifactId>aliyun-sdk-mns</artifactId>
            <version>1.1.8</version>
        </dependency>
        <dependency>
            <groupId>com.aliyun</groupId>
            <artifactId>aliyun-java-sdk-sms</artifactId>
            <version>3.0.0-rc1</version>
        </dependency>

验证码配置文件smscode.properties:

#容联云通讯短信验证码
user.smsCode.accountSid = #
user.smsCode.accountToken = #
user.smsCode.serverIp = app.cloopen.com
user.smsCode.serverPort = 8883
user.smsCode.appId = #

#短信验证码失效时间(单位秒)
user.smsCode.expires = 60
#短信验证码长度
user.smsCode.smsCodelen = 6

#阿里云AccessId
user.aliyun.smsCode.accessId=#
#阿里云AccessKey
user.aliyun.smsCode.accessKey=#
#访问MNS服务的接入地址
user.aliyun.smsCode.mnsEndpoint=#
#发送短信使用的主题
user.aliyun.smsCode.topic=sms.topic-cn-hangzhou
#发送短信使用的签名
user.aliyun.smsCode.signName=竞技世界
#注册模板Code
user.aliyun.smsCode.regTemplateCode=#
#登录模板Code
user.aliyun.smsCode.loginTemplateCode=#
#忘记密码模板Code
user.aliyun.smsCode.forgetTemplateCode=#
#绑定手机模板Code
user.aliyun.smsCode.bindTemplateCode=#
#免注账号升级模板Code
user.aliyun.smsCode.upgradeTemplateCode=#

阿里云短信配置自动加载

**
 * 
* @ClassName: AliyunSmsCodeProperties 
* @Description: TODO(阿里云短信配置) 
* @author huasc
* @date 2017年8月29日 下午1:44:51 
*
 */
@Configuration
//自动加载配置文件前缀是user.aliyun.smsCode的配置文件
@ConfigurationProperties(prefix = "user.aliyun.smsCode")
//配置文件的启用条件 当user.smsService.component的值为aliyunSmcodeService时 该配置文件才会被启用
@ConditionalOnProperty(name = "user.smsService.component",havingValue = "aliyunSmcodeService")
//配置文件地址
@PropertySource(value = "classpath:smscode.properties", encoding = "UTF-8")
public class AliyunSmsCodeProperties {

    private String accessId;

    private String accessKey;

    private String mnsEndpoint;

    private String topic;

    private String signName;

    private String regTemplateCode;

    private String loginTemplateCode;

    private String forgetTemplateCode;

    private String bindTemplateCode;
    
    private String upgradeTemplateCode;

    private CloudTopic tp;

    public String getAccessId() {
        return accessId;
    }

    public void setAccessId(String accessId) {
        this.accessId = accessId;
    }

    public String getAccessKey() {
        return accessKey;
    }

    public void setAccessKey(String accessKey) {
        this.accessKey = accessKey;
    }

    public String getMnsEndpoint() {
        return mnsEndpoint;
    }

    public void setMnsEndpoint(String mnsEndpoint) {
        this.mnsEndpoint = mnsEndpoint;
    }

    public String getTopic() {
        return topic;
    }

    public void setTopic(String topic) {
        this.topic = topic;
    }

    public String getSignName() {
        return signName;
    }

    public void setSignName(String signName) {
        this.signName = signName;
    }

    public String getRegTemplateCode() {
        return regTemplateCode;
    }

    public void setRegTemplateCode(String regTemplateCode) {
        this.regTemplateCode = regTemplateCode;
    }

    public String getLoginTemplateCode() {
        return loginTemplateCode;
    }

    public void setLoginTemplateCode(String loginTemplateCode) {
        this.loginTemplateCode = loginTemplateCode;
    }

    public String getForgetTemplateCode() {
        return forgetTemplateCode;
    }

    public void setForgetTemplateCode(String forgetTemplateCode) {
        this.forgetTemplateCode = forgetTemplateCode;
    }

    public String getBindTemplateCode() {
        return bindTemplateCode;
    }

    public void setBindTemplateCode(String bindTemplateCode) {
        this.bindTemplateCode = bindTemplateCode;
    }


    @Bean("mnsClient")
    MNSClient createClient() {
        CloudAccount account = new CloudAccount(this.accessId, this.accessKey, this.mnsEndpoint);
        MNSClient client = account.getMNSClient();
        this.client = client;
        return client;

    }

    @Bean
    CloudTopic cloudTopic(MNSClient mnsClient) {
        CloudTopic tp = mnsClient.getTopicRef(this.topic);
        this.tp = tp;
        return tp;
    }

    private MNSClient client;

    @PreDestroy
    public void destroy() {
        client.close();
    }

    public String getUpgradeTemplateCode() {
        return upgradeTemplateCode;
    }

    public void setUpgradeTemplateCode(String upgradeTemplateCode) {
        this.upgradeTemplateCode = upgradeTemplateCode;
    }

}


容联云短信配置自动加载类:


@ConfigurationProperties(prefix = "user.smsCode")
@ConditionalOnProperty(name = "user.smsService.component",havingValue = "ronglianService")
@PropertySource(value = "classpath:smscode.properties", encoding = "UTF-8")
@Configuration
public class RonglianSmsCodeProperties {

    private String accountSid;

    private String accountToken;

    private String appId;

    private String serverIp;

    private String serverPort;
    

    public String getAccountSid() {
        return accountSid;
    }

    public void setAccountSid(String accountSid) {
        this.accountSid = accountSid;
    }

    public String getAccountToken() {
        return accountToken;
    }

    public void setAccountToken(String accountToken) {
        this.accountToken = accountToken;
    }

    public String getAppId() {
        return appId;
    }

    public void setAppId(String appId) {
        this.appId = appId;
    }

    public String getServerIp() {
        return serverIp;
    }

    public void setServerIp(String serverIp) {
        this.serverIp = serverIp;
    }

    public String getServerPort() {
        return serverPort;
    }

    public void setServerPort(String serverPort) {
        this.serverPort = serverPort;
    }

    @Bean 
    public CCPRestSmsSDK createPhoneClient() {
        CCPRestSmsSDK phoneClient = new CCPRestSmsSDK();
        phoneClient.setAccount(accountSid, accountToken);
        phoneClient.init(serverIp, serverPort);
        phoneClient.setAppId(appId);
        return phoneClient;
    }
    

开启服务:

指定使用哪个短信服务,只需把响应的注释打开就可以啦。

#阿里云短信验证码平台
user.smsService.component = aliyunSmcodeService
#容联云通讯短信验证码平台
#user.smsService.component = ronglianService

这样所有的关于验证码的配置都完成了,下面是service层,我是将验证码存入到redis中并且设置有效时间,
对短信验证码进行匹配验证。如果一致的话,则将改验证码从redis库中删除掉。同时,存入的过程中设置了不同类型的key,这样通过redis客户端来查看的话就会分层显示,特别清晰。

公共部分

public abstract class SmsCodeServiceImpl implements SmsCodeService{
    
    @Autowired
    private PassportRepository passportRepository;
    
    
    abstract JedisPool getJedisPool ();
    
    @Override
    public Boolean checkSmsCode(String phoneNum, int codeType, String smsCode) {
        String smsCodeKey;
        Boolean flag = false;
        Jedis jedis = null;
        try {
            if (SmsCode.SmsCodeType.REG.value() == codeType) {
                smsCodeKey = CacheKeys.getSmsCodeRegKey(phoneNum);
            } else if (SmsCode.SmsCodeType.LOGIN.value() == codeType) {
                smsCodeKey = CacheKeys.getSmsCodeLoginKey(phoneNum);
            } else if (SmsCode.SmsCodeType.FORGET.value() == codeType) {
                smsCodeKey = CacheKeys.getSmsCodeForget(phoneNum);
            } else if (SmsCode.SmsCodeType.BIND.value() == codeType) {
                smsCodeKey = CacheKeys.getSmsCodeBind(phoneNum) ;
            } else if (SmsCode.SmsCodeType.UPGRADE.value() == codeType) {
                smsCodeKey = CacheKeys.getSmsCodeUpgrade(phoneNum);
            } else {
                return flag;
            }
            jedis = getJedisPool().getResource();
            String code = jedis.get(smsCodeKey);
            if(StringUtils.isBlank(code)){
                return flag;
            }else if (code.equals(smsCode)) {
                jedis.del(smsCodeKey);
                flag = true;
            }
            
        }  finally {
            jedis.close();
        }
        return flag;
    }
    /**
     * 检查手机号是否已经被使用
     */
    @Override
    public Boolean checkPhoneNumberIsUsed(String phoneNum){
        Passport passport = passportRepository.findByPhoneNum(phoneNum);
        if(passport == null){
            return false;
        }else{
            return true;
        }
    }
}


阿里云短信服务Service

@Service("aliyunSmcodeService")
@ConditionalOnProperty(name = "user.smsService.component", havingValue = "aliyunSmcodeService")
public class AliyunSmsCodeServiceImpl extends SmsCodeServiceImpl {

    @Value("${user.smsCode.expires}")
    private int expires;

    @Value("${user.smsCode.smsCodelen}")
    private int smsCodelen;

    @Autowired
    private JedisPool jedisPool;

    @Autowired
    private AliyunSmsCodeProperties aliyunSmsCodeProperties;

    @Autowired
    private CloudTopic topic;
    
    @Override
    JedisPool getJedisPool() {
        return jedisPool ;
    }

    /**
     * 通过阿里云发送短信验证码
     */
    @Transactional
    public SendMcodeResponse sendSmsCode(String phoneNum, int codeType) {

        RawTopicMessage msg = new RawTopicMessage();
        msg.setMessageBody("sms-message");
        MessageAttributes messageAttributes = new MessageAttributes();
        BatchSmsAttributes batchSmsAttributes = new BatchSmsAttributes();
        batchSmsAttributes.setFreeSignName(aliyunSmsCodeProperties.getSignName());
        // 短信模板code
        String templateCode = null;
        String smsCodeKey = null;

        if (SmsCode.SmsCodeType.REG.value() == codeType) {
            templateCode = aliyunSmsCodeProperties.getRegTemplateCode();
            smsCodeKey = CacheKeys.getSmsCodeRegKey(phoneNum);
        } else if (SmsCode.SmsCodeType.LOGIN.value() == codeType) {
            templateCode = aliyunSmsCodeProperties.getLoginTemplateCode();
            smsCodeKey = CacheKeys.getSmsCodeLoginKey(phoneNum);
        } else if (SmsCode.SmsCodeType.FORGET.value() == codeType) {
            templateCode = aliyunSmsCodeProperties.getForgetTemplateCode();
            smsCodeKey = CacheKeys.getSmsCodeForget(phoneNum);
        } else if (SmsCode.SmsCodeType.BIND.value() == codeType) {
            templateCode = aliyunSmsCodeProperties.getBindTemplateCode();
            smsCodeKey = CacheKeys.getSmsCodeBind(phoneNum);
        }else if (SmsCode.SmsCodeType.UPGRADE.value() == codeType) {
            templateCode = aliyunSmsCodeProperties.getUpgradeTemplateCode();
            smsCodeKey = CacheKeys.getSmsCodeUpgrade(phoneNum);
        }

        batchSmsAttributes.setTemplateCode(templateCode);
        // 设置发送短信所使用的模板中参数对应的值(第一个参数是验证码,第二个参数是过期时间)
        BatchSmsAttributes.SmsReceiverParams smsReceiverParams = new BatchSmsAttributes.SmsReceiverParams();
        String code = RandomStringUtils.randomNumeric(smsCodelen);
        smsReceiverParams.setParam("code", code);
        smsReceiverParams.setParam("time", String.valueOf(expires / 60));
        // 接收短信的手机号码
        batchSmsAttributes.addSmsReceiver(phoneNum, smsReceiverParams);
        messageAttributes.setBatchSmsAttributes(batchSmsAttributes);
        Jedis jedis = jedisPool.getResource();
        SendMcodeResponse r = null;
        try {
            // 发送消息
            TopicMessage ret = topic.publishMessage(msg, messageAttributes);
            // 将验证码信息存放于redis中并设置过期时间
            jedis.set(smsCodeKey, code);
            jedis.expire(smsCodeKey, expires);
            r = SendMcodeResponse.newBuilder().setMcode(code).setExpires(expires)
                    .setR(Result.newBuilder().setCode(Result.StatusType.SUCCESS_VALUE).setMsg("OK").build()).build();
        } catch (ServiceException se) {
            throw new ServiceException(se.getErrorCode(),se.getMessage());
        } catch (Exception e) {
            r = SendMcodeResponse.newBuilder().setMcode(code).setExpires(expires)
                    .setR(Result.newBuilder().setCode(Result.StatusType.ERROR_VALUE).setMsg(e.getMessage()).build()).build();
        } finally {
            jedis.close();
        }
        return r;
    }

    

}

容联云通讯短信Service

@Service("ronglianService")
@ConditionalOnProperty(name = "user.smsService.component", havingValue = "ronglianService")
public class RonglianSmsCodeServiceImpl  extends SmsCodeServiceImpl {

    @Autowired
    private CCPRestSmsSDK client;

    @Autowired
    private JedisPool jedisPool;

    @Value("${user.smsCode.expires}")
    private int expires;

    @Value("${user.smsCode.smsCodelen}")
    private int smsCodelen;
    

    @Override
    JedisPool getJedisPool() {
        return jedisPool;
    }

    /**
     * 发送短信验证码
     * @throws AccountException 
     */
    @Transactional
    @Override
    public SendMcodeResponse sendSmsCode(String phoneNum, int codeType) throws AccountException {
        String code = RandomStringUtils.randomNumeric(smsCodelen);
        HashMap<String, Object> message = client.sendTemplateSMS(phoneNum, String.valueOf(codeType),
                new String[] { code, String.valueOf(expires/60) });
        SendMcodeResponse r = null;
        if ("000000".equals(message.get("statusCode"))) {
            Jedis jedis = jedisPool.getResource();
            try {
                String smsCodeKey = null;
                if (SmsCode.SmsCodeType.REG.value() == codeType) {
                    smsCodeKey = CacheKeys.getSmsCodeRegKey(phoneNum);
                } else if (SmsCode.SmsCodeType.LOGIN.value() == codeType) {
                    smsCodeKey = CacheKeys.getSmsCodeLoginKey(phoneNum);
                } else if (SmsCode.SmsCodeType.FORGET.value() == codeType) {
                    smsCodeKey = CacheKeys.getSmsCodeForget(phoneNum);
                } else if (SmsCode.SmsCodeType.BIND.value() == codeType) {
                    smsCodeKey = CacheKeys.getSmsCodeBind(phoneNum);
                }
                jedis.set(smsCodeKey, code);
                jedis.expire(smsCodeKey, expires);
                r = SendMcodeResponse.newBuilder().setMcode(code).setExpires(expires)
                        .setR(Result.newBuilder().setCode(Result.StatusType.SUCCESS_VALUE).setMsg("OK").build()).build();
            } catch (Exception e) {
                r = SendMcodeResponse.newBuilder().setMcode(code).setExpires(expires)
                        .setR(Result.newBuilder().setCode(Result.StatusType.ERROR_VALUE).setMsg(e.getMessage()).build()).build();
            } finally {
                jedis.close();
            }
        } else {
            throw new AccountException(String.valueOf(message.get("statusMsg")),AccountException.ExCode.SEND_EXCEPTION.value());
        }
        return r;
    }

}

CacheKeys

public class CacheKeys {
    private final static String SSOTOKEN_PREFIX = "users:login:ssotoken:" ;
    
    private final static String SMSCODE_REG_PREFIX = "users:smscode:reg:" ;
    
    private final static String SMSCODE_LOGIN_PREFIX = "users:smscode:login:" ;
    
    private final static String SMSCODE_FORGET_PREFIX = "users:smscode:forget:" ;
    
    private final static String SMSCODE_BIND_PREFIX = "users:smscode:bind:" ;
    
    private final static String SMSCODE_UPGRADE_PREFIX = "users:smscode:upgrade:" ;
    
    private final static String USER_ALLTOKEN_PREFIX = "users:login:all:" ;
    
    private final static String EMAIL_FORGET_PREFIX = "users:emailcode:forget:" ;
    
    private final static String EMAIL_BIND_PREFIX = "users:emailcode:bind:" ;
    
    private final static String QRCODE_UUID_PREFIX = "users:qrcode:uuid:" ;
    
    private final static String QRCODE_SKEY_PREFIX = "users:qrcode:skey:" ;
    
    public static String getSsoTokenKey(String ssotoken) {
        return SSOTOKEN_PREFIX+ssotoken ;
    }
    
    public static String getSmsCodeRegKey(String phoneNum){
        return SMSCODE_REG_PREFIX+phoneNum ;
    }
    public static String getSmsCodeLoginKey(String phoneNum){
        return SMSCODE_LOGIN_PREFIX+phoneNum ;
    }
    
    public static String getSmsCodeForget(String phoneNum){
        return SMSCODE_FORGET_PREFIX+phoneNum ;
    }
    
    public static String getSmsCodeBind(String phoneNum){
        return SMSCODE_BIND_PREFIX+phoneNum ;
    }
    
    public static String getSmsCodeUpgrade(String phoneNum){
        return SMSCODE_UPGRADE_PREFIX+phoneNum ;
    }
    
    public static String getUserAllKey(String accountId){
        return USER_ALLTOKEN_PREFIX+"usr-"+accountId ;
    }
    
    public static String getEmailCodeForget(String email){
        return EMAIL_FORGET_PREFIX+email ;
    }
    
    public static String getEmailCodeBind(String email){
        return EMAIL_BIND_PREFIX+email ;
    }
    
    public static String getQrCodeUuidKey(String uuid){
        return QRCODE_UUID_PREFIX+uuid ;
    }
    
    public static String getQrCodeSkeyKey(String skey){
        return QRCODE_SKEY_PREFIX+skey ;
    }
}

到这里就全部完成了短信短信验证码的功能。


(完)

上一篇:redis介绍,redis安装,redis持久化,redis数据类型


下一篇:【汽车产业抢占AI赛道】北京汽车产业创新大集参赛项目盘点