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

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介: 目前,手机号对于我们的日常生活有着非常重要的作用,特别是手机号码实名认证以后,手机号如同你的身份证一样记录着我们的各种信息。所以短信验证码作为手机号的验证方式也显得尤为重要。因此,很多公司或网站用短信验证码来验证我们的身份信息。

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

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

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

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

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版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
5天前
|
Java API 微服务
【Spring Boot系列】通过OpenAPI规范构建微服务服务接口
【4月更文挑战第5天】通过OpenAPI接口构建Spring Boot服务RestAPI接口
60 0
|
5天前
|
Java Spring 容器
Spring系列文章:Spring6集成MyBatis3.5
Spring系列文章:Spring6集成MyBatis3.5
|
5天前
|
安全 Java 测试技术
Spring Boot集成支付宝支付:概念与实战
【4月更文挑战第29天】在电子商务和在线业务应用中,集成有效且安全的支付解决方案是至关重要的。支付宝作为中国领先的支付服务提供商,其支付功能的集成可以显著提升用户体验。本篇博客将详细介绍如何在Spring Boot应用中集成支付宝支付功能,并提供一个实战示例。
41 2
|
5天前
|
NoSQL Java MongoDB
【MongoDB 专栏】MongoDB 与 Spring Boot 的集成实践
【5月更文挑战第11天】本文介绍了如何将非关系型数据库MongoDB与Spring Boot框架集成,以实现高效灵活的数据管理。Spring Boot简化了Spring应用的构建和部署,MongoDB则以其对灵活数据结构的处理能力受到青睐。集成步骤包括:添加MongoDB依赖、配置连接信息、创建数据访问对象(DAO)以及进行数据操作。通过这种方式,开发者可以充分利用两者优势,应对各种数据需求。在实际应用中,结合微服务架构等技术,可以构建高性能、可扩展的系统。掌握MongoDB与Spring Boot集成对于提升开发效率和项目质量至关重要,未来有望在更多领域得到广泛应用。
【MongoDB 专栏】MongoDB 与 Spring Boot 的集成实践
|
5天前
|
存储 安全 开发者
【Docker 专栏】Docker 与云存储服务的集成
【5月更文挑战第9天】在数字化时代,Docker和云存储服务的结合为企业和开发者提供了强大工具。Docker的高效性、可移植性和隔离性,加上云存储的扩展性、高可靠性和高可用性,通过集成可以实现数据持久化、便捷部署和资源优化。常见的集成包括AWS S3、Azure Blob Storage和Google Cloud Storage。集成时需注意安全、性能和兼容性问题,未来集成将更加紧密和智能化,助力企业创造更大价值。
【Docker 专栏】Docker 与云存储服务的集成
|
5天前
|
安全 Java 数据库连接
在IntelliJ IDEA中通过Spring Boot集成达梦数据库:从入门到精通
在IntelliJ IDEA中通过Spring Boot集成达梦数据库:从入门到精通
|
5天前
|
Java 数据安全/隐私保护 Sentinel
微服务学习 | Spring Cloud 中使用 Sentinel 实现服务限流
微服务学习 | Spring Cloud 中使用 Sentinel 实现服务限流
|
5天前
|
存储 JSON 前端开发
Javaweb之SpringBootWeb案例之阿里云OSS服务集成的详细解析
Javaweb之SpringBootWeb案例之阿里云OSS服务集成的详细解析
23 0
|
5天前
|
Java Maven Nacos
Spring Cloud Eureka 服务注册和服务发现超详细(附加--源码实现案例--及实现逻辑图)
Spring Cloud Eureka 服务注册和服务发现超详细(附加--源码实现案例--及实现逻辑图)
31 0
|
5天前
|
缓存 Java Spring
单体项目中资源管理模块集成Spring Cache
该内容是关于将Spring Cache集成到资源管理模块以实现缓存同步的说明。主要策略包括:查询时添加到缓存,增删改时删除相关缓存。示例代码展示了@Service类中使用@Transactional和@Cacheable注解进行缓存操作,以及在RedisTemplate中处理缓存的示例。
24 5

热门文章

最新文章