• 【探花交友】注册登录需求分析、短信验证码


    文章目录

    5、注册登录需求分析

    5.1、业务说明

    5.2、需求分析

    5.3、数据库表

    5.4、实体类

    6、短信验证码

    6.1、阿里云短信服务

    6.2、模板组件

    5、注册登录需求分析

    5.1、业务说明

    用户通过手机验证码进行登录,如果是第一次登录则需要完善个人信息,在上传图片时,需要对上传的图片做人像的校验,防止用户上传非人像的图片作为头像。流程完成后,则登录成功。

    • 已注册用户:

      • 输入手机号发送验证码

      • 输入验证码,进行比对完成登录

    • 未注册用户:

      • 输入手机号发送验证码

      • 输入验证码,进行比对,自动注册(保存用户)

      • 完善用户信息

    5.2、需求分析

    服务端接受客户端请求

    Java代码调用第三方服务实现短信返送(发送短信需要运营资质,只能借助第三方实现)

    5.3、数据库表

    数据库使用的mysql:

    CREATE TABLE `tb_user` (
      `id` bigint(20) NOT NULL AUTO_INCREMENT,
      `mobile` varchar(11) DEFAULT NULL COMMENT '手机号',
      `password` varchar(32) DEFAULT NULL COMMENT '密码,需要加密',
      `created` datetime DEFAULT NULL,
      `updated` datetime DEFAULT NULL,
      PRIMARY KEY (`id`),
      KEY `mobile` (`mobile`) USING BTREE
    ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='用户表';
    ​
    CREATE TABLE `tb_user_info` (
      `id` bigint(20) NOT NULL AUTO_INCREMENT,
      `user_id` bigint(20) NOT NULL COMMENT '用户id',
      `nick_name` varchar(50) DEFAULT NULL COMMENT '昵称',
      `logo` varchar(100) DEFAULT NULL COMMENT '用户头像',
      `tags` varchar(50) DEFAULT NULL COMMENT '用户标签:多个用逗号分隔',
      `sex` int(1) DEFAULT '3' COMMENT '性别,1-男,2-女,3-未知',
      `age` int(11) DEFAULT NULL COMMENT '用户年龄',
      `edu` varchar(20) DEFAULT NULL COMMENT '学历',
      `city` varchar(20) DEFAULT NULL COMMENT '居住城市',
      `birthday` varchar(20) DEFAULT NULL COMMENT '生日',
      `cover_pic` varchar(50) DEFAULT NULL COMMENT '封面图片',
      `industry` varchar(20) DEFAULT NULL COMMENT '行业',
      `income` varchar(20) DEFAULT NULL COMMENT '收入',
      `marriage` varchar(20) DEFAULT NULL COMMENT '婚姻状态',
      `created` datetime DEFAULT NULL,
      `updated` datetime DEFAULT NULL,
      PRIMARY KEY (`id`),
      KEY `user_id` (`user_id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户信息表';
    ​

    5.4、实体类

    User

    @Data
    @AllArgsConstructor  //满参构造方法
    @NoArgsConstructor   //无参构造方法
    public class User extends BasePojo {
    ​
        private Long id;
        private String mobile;
        private String password;
    ​
        //环信用户信息
        private String hxUser;
        private String hxPassword;
    }

    6、短信验证码

    6.1、阿里云短信服务

    官方网站:短信服务_企业短信营销推广_验证码通知-阿里云

    6.1.1、申请签名与模板

    使用阿里云短信服务非常简单,仅需要简单的申请和认证即可

    阿里云登录 - 欢迎登录阿里云,安全稳定的云计算服务平台

    说明:申请签名时,个人用户只能申请一个并且签名的名称必须为“ABC商城”,否则审核不通过。

    申请模板:

    审核时间需要1~2小时,请耐心等待~

    6.1.2、示例代码

    文档:SendSms - 发送短信 - 短信服务 - 阿里云

    导入依赖:

    
        com.aliyun
        dysmsapi20170525
        2.0.1
    
    package com.tanhua.sso.service;
    ​
    import com.aliyun.dysmsapi20170525.models.SendSmsRequest;
    import com.aliyun.dysmsapi20170525.models.SendSmsResponse;
    import com.aliyun.dysmsapi20170525.models.SendSmsResponseBody;
    import com.aliyun.teaopenapi.models.Config;
    ​
    public class SendSms {
    ​
        /**
         * 使用AK&SK初始化账号Client
         * @param accessKeyId
         * @param accessKeySecret
         * @return Client
         * @throws Exception
         */
        public static com.aliyun.dysmsapi20170525.Client createClient(String accessKeyId, String accessKeySecret) throws Exception {
            Config config = new Config()
                    // 您的AccessKey ID
                    .setAccessKeyId(accessKeyId)
                    // 您的AccessKey Secret
                    .setAccessKeySecret(accessKeySecret)
                    .setEndpoint("dysmsapi.aliyuncs.com");
            // 访问的域名
            return new com.aliyun.dysmsapi20170525.Client(config);
        }
    ​
        public static void main(String[] args_) throws Exception {
            java.util.List args = java.util.Arrays.asList(args_);
            com.aliyun.dysmsapi20170525.Client client = SendSms
                    .createClient("**********", "**********");
    ​
            SendSmsRequest sendSmsRequest = new SendSmsRequest()
                    .setPhoneNumbers( "158****7944") //目标手机号
                    .setSignName("ABC商城") //签名名称
                    .setTemplateCode("SMS_204756062") //短信模板code
                    .setTemplateParam("{\"code\":\"1111\"}"); //模板中变量替换
            SendSmsResponse sendSmsResponse = client.sendSms(sendSmsRequest);
    ​
            SendSmsResponseBody body = sendSmsResponse.getBody();
    ​
            // code = OK 代表成功
            System.out.println(body.getCode() + "  " + body.getMessage());
        }
    ​
    }

    3.6.1.4、实现发送短信方法

    配置文件:aliyun.properties

    aliyun.sms.accessKeyId = ***********
    aliyun.sms.accessKeySecret = ***********
    aliyun.sms.domain= dysmsapi.aliyuncs.com
    aliyun.sms.signName= ABC商城
    aliyun.sms.templateCode= SMS_204756062

    需要注意中文编码问题:

    读取配置:

    package com.tanhua.sso.config;
    ​
    import lombok.Data;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.PropertySource;
    ​
    @Configuration
    @PropertySource("classpath:aliyun.properties")
    @ConfigurationProperties(prefix = "aliyun.sms")
    @Data
    public class AliyunSMSConfig {
    ​
        private String accessKeyId;
        private String accessKeySecret;
        private String domain;
        private String signName;
        private String templateCode;
    ​
    }

    代码实现:

    package com.tanhua.sso.service;
    ​
    import cn.hutool.core.util.RandomUtil;
    import cn.hutool.core.util.StrUtil;
    import com.aliyun.dysmsapi20170525.Client;
    import com.aliyun.dysmsapi20170525.models.SendSmsRequest;
    import com.aliyun.dysmsapi20170525.models.SendSmsResponse;
    import com.aliyun.dysmsapi20170525.models.SendSmsResponseBody;
    import com.aliyun.teaopenapi.models.Config;
    import com.tanhua.sso.config.AliyunSMSConfig;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    ​
    @Service
    @Slf4j
    public class SmsService {
    ​
        @Autowired
        private AliyunSMSConfig aliyunSMSConfig;
    ​
        /**
         * 发送短信验证码
         *
         * @param mobile
         * @return
         */
        public String sendSms(String mobile) {
            //随机生成6位数字验证码
            String code = RandomUtil.randomNumbers(6);
            try {
                Config config = new Config()
                        .setAccessKeyId(this.aliyunSMSConfig.getAccessKeyId())
                        .setAccessKeySecret(this.aliyunSMSConfig.getAccessKeySecret())
                        .setEndpoint(this.aliyunSMSConfig.getDomain());
    ​
                Client client = new Client(config);
                SendSmsRequest sendSmsRequest = new SendSmsRequest()
                        .setPhoneNumbers(mobile)//目标手机号
                        .setSignName(this.aliyunSMSConfig.getSignName()) //签名名称
                        .setTemplateCode(this.aliyunSMSConfig.getTemplateCode()) //短信模板code
                        .setTemplateParam("{\"code\":\"" + code + "\"}"); //模板中变量替换
                SendSmsResponse sendSmsResponse = client.sendSms(sendSmsRequest);
                SendSmsResponseBody body = sendSmsResponse.getBody();
                if (StrUtil.equals("OK", body.getCode())) {
                    return code;
                }
            } catch (Exception e) {
                log.error("发送短信验证码失败!" + mobile, e);
            }
            return null;
        }
    }
    ​

    6.2、模板组件

    企业开发中,往往将常见工具类封装抽取,以简洁便利的方式供其他工程模块使用。而SpringBoot的自动装配机制可以方便的实现组件抽取。SpringBoot执行流程如下

    1. 扫描依赖模块中META-INF/spring.factories

    2. 执行装配类中方法

    3. 对象存入容器中

    4. 核心工程注入对象,调用方法使用

    6.2.1、配置类

    tanhua-autoconfig创建配置信息类

    @Data
    @ConfigurationProperties(prefix = "tanhua.sms")
    public class SmsProperties {
        private String signName;
        private String templateCode;
        private String accessKey;
        private String secret;
    }

    6.2.2、发送短信模板对象

    tanhua-autoconfig创建模板对象对象发送信息

    package com.tanhua.autoconfig.template;
    ​
    import com.aliyun.dysmsapi20170525.models.SendSmsRequest;
    import com.aliyun.dysmsapi20170525.models.SendSmsResponse;
    import com.aliyun.dysmsapi20170525.models.SendSmsResponseBody;
    import com.aliyun.teaopenapi.models.Config;
    import com.tanhua.autoconfig.properties.SmsProperties;
    ​
    public class SmsTemplate {
    ​
        private SmsProperties properties;
    ​
        public SmsTemplate(SmsProperties properties) {
            this.properties = properties;
        }
    ​
        public void sendSms(String mobile,String code) {
    ​
            try {
                //配置阿里云
                Config config = new Config()
                        // 您的AccessKey ID
                        .setAccessKeyId(properties.getAccessKey())
                        // 您的AccessKey Secret
                        .setAccessKeySecret(properties.getSecret());
                // 访问的域名
                config.endpoint = "dysmsapi.aliyuncs.com";
    ​
                com.aliyun.dysmsapi20170525.Client client =  new com.aliyun.dysmsapi20170525.Client(config);
    ​
                SendSmsRequest sendSmsRequest = new SendSmsRequest()
                        .setPhoneNumbers(mobile)
                        .setSignName(properties.getSignName())
                        .setTemplateCode(properties.getTemplateCode())
                        .setTemplateParam("{\"code\":\""+code+"\"}");
                // 复制代码运行请自行打印 API 的返回值
                SendSmsResponse response = client.sendSms(sendSmsRequest);
    ​
                SendSmsResponseBody body = response.getBody();
    ​
                System.out.println(body.getMessage());
    ​
            }catch (Exception e) {
                e.printStackTrace();
            }
    ​
        }
    ​
    }

    6.2.3、自动装配类

    tanhua-autoconfig创建自动装配的配置类

    package com.tanhua.autoconfig;
    ​
    ​
    import com.tanhua.autoconfig.properties.*;
    import com.tanhua.autoconfig.template.*;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
    import org.springframework.boot.context.properties.EnableConfigurationProperties;
    import org.springframework.context.annotation.Bean;
    ​
    @EnableConfigurationProperties({
            SmsProperties.class
    })
    public class TanhuaAutoConfiguration {
    ​
        @Bean
        public SmsTemplate smsTemplate(SmsProperties properties) {
            return new SmsTemplate(properties);
        }
    }

    6.2.4、自动装配配置

    根据自动装配原则,在tanhua-autoconfig工程创建 /META-INF/spring.factories文件

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    com.tanhua.autoconfig.TanhuaAutoConfiguration

    6.2.5、测试

    tanhua-app-server工程加入短信配置

    tanhua:
      sms:
        signName: 物流云商
        templateCode: SMS_106590012
        accessKey: LTAI4GKgob9vZ53k2SZdyAC7
        secret: LHLBvXmILRoyw0niRSBuXBZewQ30la

    编写单元测试类

    @RunWith(SpringRunner.class)
    @SpringBootTest(classes = AppServerApplication.class)
    public class SmsTemplateTest {
    ​
        //注入
        @Autowired
        private SmsTemplate smsTemplate;
    ​
        //测试
        @Test
        public void testSendSms() {
            smsTemplate.sendSms("18618412321","4567");
        }
    }

    3.6.2、SSO短信接口服务

    3.6.2.1、mock接口

    地址:YApi-高效、易用、功能强大的可视化接口管理平台

    3.6.2.2、编写接口服务

    编写ErrorResult,ErrorResult对象是与前端约定好的结构,如果发生错误需要返回该对象,如果未发生错误响应200即可。

    package com.tanhua.sso.vo;
    ​
    import lombok.Builder;
    import lombok.Data;
    ​
    @Data
    @Builder
    public class ErrorResult {
    ​
        private String errCode;
        private String errMessage;
    }
    ​

    SmsController:

    package com.tanhua.sso.controller;
    ​
    import com.tanhua.sso.service.SmsService;
    import com.tanhua.sso.vo.ErrorResult;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.ResponseEntity;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    ​
    import java.util.Map;
    ​
    @RestController
    @RequestMapping("user")
    @Slf4j
    public class SmsController {
    ​
        @Autowired
        private SmsService smsService;
    ​
        /**
         * 发送短信验证码接口
         *
         * @param param
         * @return
         */
        @PostMapping("login")
        public ResponseEntity sendCheckCode(@RequestBody Map param) {
            ErrorResult errorResult = null;
            String phone = param.get("phone");
            try {
                errorResult = this.smsService.sendCheckCode(phone);
                if (null == errorResult) {
                    return ResponseEntity.ok(null);
                }
            } catch (Exception e) {
                log.error("发送短信验证码失败~ phone = " + phone, e);
                errorResult = ErrorResult.builder().errCode("000002").errMessage("短信验证码发送失败!").build();
            }
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResult);
        }
    ​
    }
    ​

    SmsService:

    package com.tanhua.sso.service;
    ​
    import cn.hutool.core.util.RandomUtil;
    import cn.hutool.core.util.StrUtil;
    import com.aliyun.dysmsapi20170525.Client;
    import com.aliyun.dysmsapi20170525.models.SendSmsRequest;
    import com.aliyun.dysmsapi20170525.models.SendSmsResponse;
    import com.aliyun.dysmsapi20170525.models.SendSmsResponseBody;
    import com.aliyun.teaopenapi.models.Config;
    import com.tanhua.sso.config.AliyunSMSConfig;
    import com.tanhua.sso.vo.ErrorResult;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.stereotype.Service;
    ​
    import java.time.Duration;
    ​
    @Service
    @Slf4j
    public class SmsService {
    ​
        @Autowired
        private AliyunSMSConfig aliyunSMSConfig;
    ​
        @Autowired
        private RedisTemplate redisTemplate;
    ​
        /**
         * 发送短信验证码
         *
         * @param mobile
         * @return
         */
        public String sendSms(String mobile) {
            //随机生成6位数字验证码
            String code = RandomUtil.randomNumbers(6);
            try {
                Config config = new Config()
                        .setAccessKeyId(this.aliyunSMSConfig.getAccessKeyId())
                        .setAccessKeySecret(this.aliyunSMSConfig.getAccessKeySecret())
                        .setEndpoint(this.aliyunSMSConfig.getDomain());
    ​
                Client client = new Client(config);
                SendSmsRequest sendSmsRequest = new SendSmsRequest()
                        .setPhoneNumbers(mobile)//目标手机号
                        .setSignName(this.aliyunSMSConfig.getSignName()) //签名名称
                        .setTemplateCode(this.aliyunSMSConfig.getTemplateCode()) //短信模板code
                        .setTemplateParam("{\"code\":\"" + code + "\"}"); //模板中变量替换
                SendSmsResponse sendSmsResponse = client.sendSms(sendSmsRequest);
                SendSmsResponseBody body = sendSmsResponse.getBody();
                if (StrUtil.equals("OK", body.getCode())) {
                    return code;
                }
            } catch (Exception e) {
                log.error("发送短信验证码失败!" + mobile, e);
            }
            return null;
        }
    ​
        /**
         * 发送短信验证码
         * 实现:发送完成短信验证码后,需要将验证码保存到redis中
         * @param phone
         * @return
         */
        public ErrorResult sendCheckCode(String phone) {
            String redisKey = "CHECK_CODE_" + phone;
    ​
            //先判断该手机号发送的验证码是否还未失效
            if(this.redisTemplate.hasKey(redisKey)){
                String msg = "上一次发送的验证码还未失效!";
                return ErrorResult.builder().errCode("000001").errMessage(msg).build();
            }
    ​
            String code = this.sendSms(phone);
            if(StrUtil.isEmpty(code)){
                String msg = "发送短信验证码失败!";
                return ErrorResult.builder().errCode("000000").errMessage(msg).build();
            }
    ​
            //短信发送成功,将验证码保存到redis中,有效期为5分钟
            this.redisTemplate.opsForValue().set(redisKey, code, Duration.ofMinutes(5));
    ​
            return null;
        }
    }
    
    
  • 相关阅读:
    融合边界处理机制的学习型麻雀搜索算法
    【MySQL系列】MySQL的事务管理的学习(二)_ 再次理解隔离性
    百度知道APP心跳包分析-MD5字段(gzip + CRC32)
    面试突击46:公平锁和非公平锁有什么区别?
    将写的项目部署到Linux服务器上
    QT创建子目录项目,可以让项目组织成树形结构的示例:在项目中同时创建Application和第三方动态库(内部)
    Eviews如何做VAR
    python中not的用法
    Create Engaging Diagrams with Animation
    系统设计中的缓存技术:完整指南
  • 原文地址:https://blog.csdn.net/weixin_45481821/article/details/126848887