美文网首页springbootconfigspringbootfeign
spring-boot | 集成短信验证码服务

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

作者: 代码_搬运工 | 来源:发表于2017-08-29 14:42 被阅读607次

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

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

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

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

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

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


    (完)

    相关文章

      网友评论

      • b35ca2af9c19:SendMcodeResponse.newBuilder().setMcode(code).setExpires(expires)
        .setR(Result.newBuilder().setCode(Result.StatusType.ERROR_VALUE).setMsg(e.getMessage()).build()).build();中的这个SendMcodeResponse类麻烦作者贴出来一下,代码有点不全,自己不知如何创建内容,谢谢
        代码_搬运工:@Superman宁 这个是protobuf的文件 很少有人用 和验证码是没有关联的
      • b35ca2af9c19:SmsCodeServiceImpl的接口类就是两个方法的话,就不麻烦作者贴出,希望作者能告知SmsCode.SmsCodeType.REG.value()是如何得来的,非常感谢
        b35ca2af9c19:@代码_搬运工 非常感谢你的帮助
        :+1:
        代码_搬运工:这个是实体类中的一个枚举类型:
        public static enum SmsCodeType{
        REG("注册", 0) ,
        LOGIN("登录", 1),
        FORGET("忘记密码", 2),
        BIND("绑定手机",3),
        UPGRADE("免注账号升级",4);
        private String name;
        private int value;

        public int value() {
        return value;
        }

        public String getName() {
        return name;
        }

        private SmsCodeType(String name, int value) {
        this.name = name;
        this.value = value;
        }
        @Override
        public String toString() {
        return this.value + "_" + this.name;
        }
        }
      • b35ca2af9c19:你好,请问一下这个SmsCodeServiceImpl 的接口类并没有贴出来,还有这个方法中SmsCode.SmsCodeType.REG.value()的SmsCode是jar包有的,还是需自己编写

      本文标题:spring-boot | 集成短信验证码服务

      本文链接:https://www.haomeiwen.com/subject/zydodxtx.html