【探花交友】注册登录需求分析

文章目录

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 - 发送短信 - 短信服务 - 阿里云

导入依赖:

<dependency>
    <groupId>com.aliyun</groupId>
    <artifactId>dysmsapi20170525</artifactId>
    <version>2.0.1</version>
</dependency>
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<String> 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<ErrorResult> sendCheckCode(@RequestBody Map<String, String> 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<String, String> 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;
    }
}

posted @ 2022-11-09 08:11  陶然同学  阅读(35)  评论(0)    收藏  举报