spring-boot | 集成短信验证码服务-阿里云开发者社区

开发者社区> huasc> 正文

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

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


(完)

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
SpringBoot实战(三)之使用RestFul Web服务
一、导入maven依赖 4.0.0 org.springframework gs-consuming-rest 0.1.0 org.springframework.
1252 0
springboot2.x中的服务监控之邮件预警
上一篇文章写了怎么在springboot2.x中添加服务监控,如果有需要的小伙伴可以回头看一眼,《springboot2.x中的服务监控》,可是这还需要我时不时的来登录一下系统查看一下,好麻烦,要是能有提醒就好了。
988 0
springboot2.x中的服务监控
想给服务添加一个监控,看看网上各位前辈的,基本都是基于springboot1.x的,springboot升级到2.0以后和1.x还是有很多不一样的,那么2.0以后怎么使用admin监控呢? 先看下图的managment.security.enable,现在已经是过时API了,那么我们必须要更新知识库了。
1241 0
阿里云服务器端口号设置
阿里云服务器初级使用者可能面临的问题之一. 使用tomcat或者其他服务器软件设置端口号后,比如 一些不是默认的, mysql的 3306, mssql的1433,有时候打不开网页, 原因是没有在ecs安全组去设置这个端口号. 解决: 点击ecs下网络和安全下的安全组 在弹出的安全组中,如果没有就新建安全组,然后点击配置规则 最后如上图点击添加...或快速创建.   have fun!  将编程看作是一门艺术,而不单单是个技术。
4602 0
SpringBoot开发案例之整合mail发送服务
记得上个月做过这样一篇笔记,微服务架构实践之邮件通知系统改造。 当时用的是开源的第三方插件mail和Thymeleaf做发送服务,显然这样子你需要配置一些东西,麻烦!!!接触了Spring Boot以后,显然有更好的解决方案,我们只需要引入spring-boot-starter-mail模块就实现了自动化配置。
20445 0
+关注
huasc
专注于java spring spring boot spring cloud docker 领域的研究
18
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
文娱运维技术
立即下载
《SaaS模式云原生数据仓库应用场景实践》
立即下载
《看见新力量:二》电子书
立即下载