若依客户端注册相关部分全解析
注册起始业务线(第一部分)
依旧是随便输入一些数据然后去看地址
![image]()
很明显和上回的登录是在一个服务里的去看看代码,先来看看controller
if (registerBody.getType().equals(TokenConstants.ADMIN)) {
sysLoginService.register(registerBody.getUsername(), registerBody.getPassword());
} else {
pcLoginService.register(registerBody);
}
return R.ok();
这里对前端传来的注册对象进行了类型的判断,如果值是Amin(管理),意味着注册的是管理系统而非客户端,今天主要看客户端,按ctrl跟进
// 用户名或密码或昵称或验证码是否为空 错误
if (StringUtils.isAnyBlank(register.getUsername(), register.getPassword(),register.getNickname(),register.getVerifyCode())) {
throw new ServiceException("用户/密码/验证码必须填写");
}
if (register.getPassword().length() < UserConstants.PASSWORD_MIN_LENGTH
|| register.getPassword().length() > UserConstants.PASSWORD_MAX_LENGTH) {
throw new ServiceException("密码长度必须在5到20个字符之间");
}
//手机号码格式判断
if(!RegexUtils.isPhoneLegal(register.getUsername())){
throw new ServiceException("手机号码格式错误");
}
![image]()
代码里首先对用户名,手机号,密码和验证码进行了非空校验,然后对密码长度和手机号是否合法进行了校验然后需要完成验证码相关部分,这里采用了与redis中收到的验证码进行对比的方式来完成,这里只在这个服务里写业务已经是远远不够了,我们厘清一下思路,首先我们需要写一个发送验证码的服务,然后去redis的服务中完成这个验证码的存储功能,这是大体的业务思路。
发送短信的服务(第二部分)
和之前的操作是一样的,新建模块然后导入依赖,这里需要仔细思考需要导入什么依赖,nacos的config和discovery(服务发现和配置中心),opnefeign(远程调用),loadBalancer(负载均衡),spring -boot-starter-web(web场景启动器),actuator(监控组件),然后还有一些依赖复用具体如下
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>cn.wolfcode.wolf2w</groupId>
<artifactId>travel-common-core</artifactId>
</dependency>
<dependency>
<groupId>cn.wolfcode.wolf2w</groupId>
<artifactId>travel-common-log</artifactId>
</dependency>
<dependency>
<groupId>cn.wolfcode.wolf2w</groupId>
<artifactId>travel-common-redis</artifactId>
</dependency>
<dependency>
<groupId>cn.wolfcode.wolf2w</groupId>
<artifactId>travel-common-security</artifactId>
</dependency>
上面有一些我提到的依赖并没有出现在依赖中是因为在依赖复用中囊括了,重复引用反而会报错,写完依赖就是配置类了,根据依赖中的内容配一些能保证功能的使用就可以
server:
port: 8087
spring:
application:
name: travel-message
cloud:
nacos:
discovery:
server-addr: localhost:8848
redis:
host: localhost
password: 123456
port: 6379
这样前置准备就做好了,接下来就是业务线了,我们想发送代码到用户的手机里需要运营商的协助,这显然不是我们的能解决的,这里可以去阿里云的市场里去搜索短信对应的内容,找一个口碑比较好的接口把他的代码拷贝下来
![image]()
毕竟是付费的,我们把它做成工具类就好,不然要花钱的(苦逼大学生发不起短信),所以我们在业务线简单模拟一个随机的四位数验证码来模拟业务就好了,然后将验证码缓存到redis中,这里其实是有一个痛点的,就是我们每次缓存的时候都需要硬编码setCacheObject("verifycode:130xxxxxxxx"),着实费劲,所以我们就需要到第三部分了
redis枚举类(第三部分)
说起来只背过枚举类的概念,还是第一次自己上手去写枚举类,java中的枚举类本质还是类,我们需要完成是把重复的内容作为类成员封装起来以便于每次使用,然后提供拼接的方法。在枚举类中默认为私有的构造器,每次创建枚举常量的时候进行赋值
@Getter
public enum RedisKeys {
//验证码
VERIFY_CODE("verify_code",60*3);
private String prefix; //统一前缀
private long time; //过期时间
private RedisKeys(String prefix, long time) {
this.prefix = prefix;
this.time = time;
}
/**
* 拼接key
* 返回值:拼接后的key
*/
public String join(String... values){
StringBuffer sb = new StringBuffer();
sb.append(this.prefix);
for (String value : values) {
sb.append(":").append(value);
}
return sb.toString();
}
}
VERIFY_CODE("verify_code",60*3)设置了规定的常量值,然后写了一个拼接字符串的join方法,(String... values)这个是可变字符的串意思。
一般字符串的拼接选择都是StringBuffer,先拼接前缀然后循环遍历value值中间用:拼接value,最终的效果就是前缀:值这样,这样添加缓存就方便多了,程序员果然还是懒一点的好啊。
发送信息收尾(第四部分)
上面讲到了将发送的验证码缓存到redis,这里我们有了枚举类封装好了前缀以及拼接方式就好办了
//缓存验证码
String key = RedisKeys.VERIFY_CODE.join(phone);
String value = verifyCode.toString();
redisService.setCacheObject(key,value,RedisKeys.VERIFY_CODE.getTime(), TimeUnit.SECONDS);
存储方式是key,value,key值是前缀拼接电话号码,这样才能对应准确的验证码,然后值就是用random随机生成的一个四位数,然后将key和value放进redis,并设置过期时间(在枚举类中设置了60*3,三分钟)。这样验证码的发送以及在缓存中的存储部分就完成了
注册部分收尾(最终部分)
在上文中,已经完成了注册数据的参数合理化验证,下面对该电话号码的对应的验证码进行查询然后与用户填写的验证进行比对,也就是判断register.getVerify和redisService。getCacheObject(key)是否相等
String key = RedisKeys.VERIFY_CODE.join(register.getUsername());
String value = redisService.getCacheObject(key);
if(StringUtils.isBlank(value)){
throw new ServiceException("验证码不存在");
}
//再进行等值判断
if(!value.equalsIgnoreCase(verifyCode)){
throw new ServiceException("验证码错误");
}
这里需要忽略大小写因为有些验证码是有英文字母的,下一步将注册信息封装,使用远程的service新增到数据库去
UserInfo userInfo = new UserInfo();
userInfo.setPhone(register.getUsername());
userInfo.setNickname(register.getNickname());
String pwd = SecurityUtils.encryptPassword(register.getPassword());
userInfo.setPassword(pwd);
System.out.println(pwd);
userInfo.setRegisterTime(new Date());
userInfo.setRegisterIp(IpUtils.getIpAddr());
R<Boolean> registerResult = remoteUserInfoService.registerUserInfo(userInfo, SecurityConstants.INNER);
完成之后一定要记得安全性验证,因为新增有可能插入空进去所以最后一步就是对registerResult.getData(),对数据部分进行非空验证
if (!registerResult.getData()) {
log.error(registerResult);
throw new ServiceException(registerResult.getMsg());
}
这样注册功能就完成了我们来测试一下,此时,如果手机号码是第一次使用的话,那么注册时没问题的,如果再此注册相同的手机号,就会报错
![image]()
这是由于我们并没有对手机号也就是账号是否在数据库中存在进行验证,少了那一步,之所以registerResult是Boolean类型的就是代表了,他是判断数据库中是否有这个手机号(username)的,所以需要加上
if(R.isError(registerResult)){
throw new ServiceException(registerResult.getMsg());
}
对registerResult对象的false验证,这样注册的功能就基本完成了