关于App项目的 - 短信登录注册业务接口对接
整体架构
- 整体架构查看脑图
- 课程大纲
01、 搭建一个项目父工程
项目在一个父子工程 【xq-pugs-travel】
- 修改配置pom.xml的packing 改成:pom即可
- 导入依赖和管理
整个pom.xml如下:
<packaging>pom</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<mysql.version>8.0.26</mysql.version>
<mybatisplus.version>3.5.0</mybatisplus.version>
<okhttp.version>4.2.2</okhttp.version>
<jackson.version>2.10.2</jackson.version>
<commons-codec.version>1.11</commons-codec.version>
<commons-lang3.version>3.4</commons-lang3.version>
<commons-fileupload.version>1.4</commons-fileupload.version>
<google-guava.version>31.0.1-jre</google-guava.version>
<springboot.version>2.5.8</springboot.version>
<lombok.version>1.18.22</lombok.version>
<slf4j.version>1.7.21</slf4j.version>
<joda-time.version>2.10.6</joda-time.version>
</properties>
<!--
使用dependencyManagement的目的是为了保证父工程的干净,
也就是说父工程他只负责管理依赖,以及依赖的版本,而不会导入额外的jar依赖。
如此一来父工程的职责就很单一了,而且也符合了面向对象开发的父子继承关系,
依赖的导入只有在各自的子工程中才会进行导入。
-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${springboot.version}</version>
<scope>import</scope>
<type>pom</type>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatisplus.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<!-- jackson -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<!-- apache 工具类 -->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>${commons-codec.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons-lang3.version}</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>${commons-fileupload.version}</version>
</dependency>
<!-- google 工具类 -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${google-guava.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.79</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
<!-- kaptcha验证码 -->
<dependency>
<groupId>com.github.penggle</groupId>
<artifactId>kaptcha</artifactId>
<version>2.3.2</version>
</dependency>
<!-- joda-time 时间工具 -->
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>${joda-time.version}</version>
</dependency>
<!-- MinIO -->
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.2.1</version>
</dependency>
<!-- Token生成与解析-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.10.3</version>
</dependency>
<!-- oss -->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.10.2</version>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<!-- Java 编译 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.2</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
02、创建对应模块
- 契约工程 – pojo / dto / vo / bo
- xq-pugs-model
- 契约工程2 – interface
- xq-pugs-interface
- 服务工程 - service
- xq-pugs-service
- 通用服务工程 - common一些:工具utils
- xq-pugs-commons
- 数据服务工程 – dao/mapper
- xq-pugs-mapper
- api工程 – web工程
- xq-pugs-app-api
xq-pugs-app-api —> xq-pugs-service —> xq-pugs-mapper —->xq-pugs-model—–>xq-pugs-commons
03、整合帮助文档 - Knife4j
官网:https://doc.xiaominfo.com/
knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案
1:依赖
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>2.0.7</version>
</dependency>
第二步:创建Swagger配置依赖,代码如下:
@Configuration
@EnableSwagger2WebMvc
public class Knife4jConfiguration {
@Bean(value = "defaultApi2")
public Docket defaultApi2() {
Docket docket=new Docket(DocumentationType.SWAGGER_2)
.apiInfo(new ApiInfoBuilder()
//.title("swagger-bootstrap-ui-demo RESTful APIs")
.description("# swagger-bootstrap-ui-demo RESTful APIs")
.termsOfServiceUrl("http://www.xx.com/")
.contact("xx@qq.com")
.version("1.0")
.build())
//分组名称
.groupName("2.X版本")
.select()
//这里指定Controller扫描包路径
.apis(RequestHandlerSelectors.basePackage("com.github.xiaoymin.knife4j.controller"))
.paths(PathSelectors.any())
.build();
return docket;
}
}
最終整个工程目录结构如下图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0MvXyKU0-1651822205200)(asserts/codej.png)]
IndexController.java
包含一个简单的RESTful接口,代码示例如下:
@Api(tags = "首页模块")
@RestController
public class IndexController {
@ApiImplicitParam(name = "name",value = "姓名",required = true)
@ApiOperation(value = "向客人问好")
@GetMapping("/sayHi")
public ResponseEntity<String> sayHi(@RequestParam(value = "name")String name){
return ResponseEntity.ok("Hi:"+name);
}
}
此时,启动Spring Boot工程,在浏览器中访问:http://localhost:8866/doc.html
界面效果图如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nT4cJqBw-1651822205202)(asserts/fast_index.png)]
04、VO DTO BO POJO的关系
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-q2ARu2PT-1651822205202)(asserts/image-20220317002430957.png)]
05、手机短信发送的API的对接工作
-
步骤1:对接阿里云短信或者腾讯云短信服务
-
需要企业资质,才能申请下来,个人不能申请
-
打开阿里云的:短信服务 : https://dysms.console.aliyun.com/overview
依赖
<dependency> <groupId>com.aliyun</groupId> <artifactId>dysmsapi20170525</artifactId> <version>2.0.9</version> </dependency>
代码
package com.pug.service.sms; import com.aliyun.dysmsapi20170525.models.SendSmsRequest; import com.aliyun.teaopenapi.models.Config; import org.springframework.stereotype.Component; @Component public class SMSUtils { /** * 使用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); // 访问的域名 config.endpoint = "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 = SMSUtils.createClient("LTAI5t63HWGeGJa9XUgzsxp4", "rkwNORu5CmeUHGKAi0gvWt9N7zXQ7N"); SendSmsRequest sendSmsRequest = new SendSmsRequest() .setPhoneNumbers("15074816437") .setSignName("同学你好网络科技") .setTemplateCode("SMS_235792419") .setTemplateParam("{\"code\":\"123456\"}"); // 复制代码运行请自行打印 API 的返回值 client.sendSms(sendSmsRequest); } }
获取AK
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-btHzlqJ8-1651822205203)(asserts/image-20220317005458739.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-alJPUSO0-1651822205203)(asserts/image-20220317005553323.png)]
-
步骤2:把发送短息。发送成功以后存储到redis中
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ntKLPVU8-1651822205204)(asserts/image-20220317004222046.png)]
代码
package com.pug.web.sms;
import com.pug.service.sms.SmsService;
import com.pug.utils.fn.asserts.Vsserts;
import com.pug.utils.valid.ValidatorUtil;
import com.pug.web.BaseController;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author 飞哥
* @Title: 学相伴出品
* @Description: 飞哥B站地址:https://space.bilibili.com/490711252
* 记得关注和三连哦!
* @Description: 我们有一个学习网站:https://www.kuangstudy.com
* @date 2022/3/16$ 23:56$
*/
@Api(tags = "短信发送")
@RestController
@RequiredArgsConstructor
public class PassportSMSSendController extends BaseController {
private final SmsService smsService;
@ApiOperation("根据手机号码发送短信")
@PostMapping("/passport/smssend/{phone}")
public String sendPhoneCode(@PathVariable("phone") String phone) {
Vsserts.isEmptyEx(phone, "请输入手机号码");
Vsserts.isFalse(ValidatorUtil.isValidatorPhone(phone),"请输入正确的手机号码");
// 1: 生成一个发送短信的随机数
String code = (int) ((Math.random() * 9 + 1) * 100000) + "";
// 2: 发送短信的接口
boolean sendSMS = smsService.sendSMS(phone, code);
// 把生成的验证码放入到redis缓存中,用于后续的验证
redisOperator.set(PUG_XQ_LOGIN_SMS_CODE + phone, code);
// 发送短信成功
return "success";
}
}
06、编写短信登录的接口
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NV0KDdD9-1651822205205)(asserts/image-20220317004222046.png)]
1:准备LoginVo接受App传递过来的参数
package com.pug.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
/**
* @author 飞哥
* @Title: 学相伴出品
* @Description: 飞哥B站地址:https://space.bilibili.com/490711252
* 记得关注和三连哦!
* @Description: 我们有一个学习网站:https://www.kuangstudy.com
* @date 2022/3/17$ 0:10$
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel
public class KssUserVo {
@NotNull(message = "请输入手机号码")
@Length(max = 11,min = 11,message = "请输入11位手机号码")
@ApiModelProperty(value = "手机号码")
@Pattern(regexp = "(^0?1[1|2|3|4|5|7|6|8|9][0-9]\\d{8}$)",message = "请输入正确的手机号码")
private String phone;
@ApiModelProperty(value = "短信码")
@NotEmpty(message = "请输入短信码")
private String smscode;
}
2:开始处理逻辑
package com.pug.web.login;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.pug.bo.KssUserBo;
import com.pug.pojo.KssUser;
import com.pug.result.ex.KsdBusinessException;
import com.pug.service.idwork.IdWorkService;
import com.pug.service.user.IUserService;
import com.pug.utils.fn.asserts.Vsserts;
import com.pug.vo.KssUserVo;
import com.pug.web.BaseController;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.Date;
import java.util.UUID;
/**
* @author 飞哥
* @Title: 学相伴出品
* @Description: 飞哥B站地址:https://space.bilibili.com/490711252
* 记得关注和三连哦!
* @Description: 我们有一个学习网站:https://www.kuangstudy.com
* @date 2022/3/16$ 23:56$
*/
@Api(tags = "登录管理")
@RestController
@RequiredArgsConstructor
public class PassportLoginController extends BaseController {
private final IUserService userService;
private final IdWorkService idWorkService;
@ApiOperation("登录方法")
@PostMapping("/passport/login")
public KssUserBo logined(@RequestBody @Valid KssUserVo kssUser) {
// 1: 接受用户提交用户手机和短信
String inputPhone = kssUser.getPhone();
String inputSmsCode = kssUser.getSmscode();
// 2: 获取redis缓存中的存储短信
String cacheSmsCode = redisOperator.get(PUG_XQ_LOGIN_SMS_CODE + inputPhone);
Vsserts.isEmptyEx(cacheSmsCode, "短信已经失效");
// 3: 对比短信码是否正确
if (!cacheSmsCode.equalsIgnoreCase(inputSmsCode)) {
throw new KsdBusinessException(602, "你输入短信证码有误!");
}
// 4:开始进行业务的处理
KssUser dbUser = userService.getByPhone(inputPhone);
// 如果dbUser,说明没有注册
if (Vsserts.isNull(dbUser)) {
// 开始注册
dbUser = new KssUser();
dbUser.setPhone(inputPhone);
dbUser.setIdcode(idWorkService.nextShort());
dbUser.setNickname("小伴");
dbUser.setPassword("");
dbUser.setAvatar("aa.jpg");
dbUser.setSex(2);
dbUser.setCountry("中国");
dbUser.setProvince("");
dbUser.setCity("");
dbUser.setDistrict("");
dbUser.setDescription("Ta什么都没有留下...");
dbUser.setBgImg("biimg.jpg");
dbUser.setStatus(1);
userService.saveOrUpdate(dbUser);
}
// 5: 存在就返回
KssUserBo kssUserBo = new KssUserBo();
BeanUtils.copyProperties(dbUser, kssUserBo);
// 6: 生成一个uuid代表token
String uuid = UUID.randomUUID().toString();
kssUserBo.setToken(uuid);
return kssUserBo;
}
}