依旧是之前的分析图
1、后端
- 注册前需要再次进行服务端校验
- 用户名是否已被注册
- 手机号是否已被注册
- 验证码是否错误
- 验证码是否实现
- 密码使用 BCrypt 进行加密
Controller层
关于根据用户名校验和手机号校验的方法这里没有贴上去,简单的查询判断
package com.czxy.controller;
import com.czxy.pojo.User;
import com.czxy.service.UserService;
import com.czxy.vo.BaseResult;
import org.springframework.data.redis.core.StringRedisTemplate;
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 javax.annotation.Resource;
/**
* @author 庭前云落
* @Date 2020/3/20 21:52
* @description
*/
@RestController
@RequestMapping("/user")
public class UserController {
@Resource
private UserService userService;
@Resource
private StringRedisTemplate stringRedisTemplate;
@PostMapping("/register")
public BaseResult register(@RequestBody User user) {
//服务端校验
User result = userService.findByUsername(user.getUsername());
if (result != null) {
return BaseResult.error("用户名已经存在");
}
result = userService.findByMobile(user.getMobile());
if (result != null) {
return BaseResult.error("该手机号已被注册");
}
//验证码
String code = stringRedisTemplate.opsForValue().get("sms_register" + user.getMobile());
if (code == null) {
return BaseResult.error("验证码无效");
}
if (!code.equalsIgnoreCase(user.getCode())) {
return BaseResult.error("验证码不正确");
}
//注册
userService.register(user);
// 注册完成后删除redis中的验证码
stringRedisTemplate.delete("sms_register" + user.getMobile());
return BaseResult.ok("注册成功");
}
}
注意一下:注册完成后删除 redis 中的验证码,一定放在判断语句后,不然验证码如果无意输错,点击了注册按钮,没经过判断 redis 里面就直接删了,之后再输入正确的,就算没到5分钟的失效时间也会 return "验证码失效"。日常滚雷
UserServiceImpl
使用BCrypt 密码加密
package com.czxy.service.Impl;
import com.czxy.mapper.UserMapper;
import com.czxy.pojo.User;
import com.czxy.service.UserService;
import com.czxy.utils.BCrypt;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Date;
/**
* @author 庭前云落
* @Date 2020/3/20 21:47
* @description
*/
@Service
public class UserServiceImpl implements UserService {
@Resource
private UserMapper userMapper;
/**
* 用户注册
*/
public void register(User user){
//密码加密
String password = BCrypt.hashpw(user.getPassword());
user.setPassword(password);
//注册时间
user.setCreatedAt(new Date());
user.setUpdatedAt(user.getCreatedAt());
//注册(添加)
userMapper.insert(user);
}
}
2、前端
前端使用的是 Nuxt.js ,如果想要入门学习,点击前往 Nuxt.js学习(一) --- 让你清晰了解Nuxt.js、Nuxt.js环境搭建,完整的一套,系统的全面学习。
前端就没什么,一些校验,调用后端接口,依旧是上代码
Register.vue
<template>
<div>
<TopNav></TopNav>
<div style="clear:both;"></div>
<HeaderLogo></HeaderLogo>
<div style="clear:both;"></div>
<!-- 正文 -->
<!-- 注册内容start -->
<div class="login w990 bc mt10 regist">
<div class="login_hd">
<h2>用户注册</h2>
<b></b>
</div>
<div class="login_bd">
<div class="login_form fl">
<form action method="post">
<ul>
<li>
<label for>用户名:</label>
<input
type="text"
class="txt"
v-model="user.username"
name="username"
@blur="checkUsername"
/>
<p>3-20位字符,可由中文、字母、数字和下划线组成</p>
<p :class="userMsg.username.code==1?'success':'error'">{{userMsg.username.message}}</p>
</li>
<li>
<label for>密码:</label>
<input
type="password"
class="txt"
name="password"
v-model="user.p1"
@blur="checkpassword1"
/>
<p>6-20位字符,可使用字母、数字和符号的组合,不建议使用纯数字、纯字母、纯符号</p>
<p v-if="userMsg.password1.code==0" class="error">{{userMsg.password1.message}}</p>
</li>
<li>
<label for>确认密码:</label>
<input
type="password"
class="txt"
name="password"
v-model="user.p2"
@blur="checkpassword2"
/>
<p>请再次输入密码</p>
<p v-if="userMsg.password2.code==0" class="error">{{userMsg.password2.message}}</p>
</li>
<li>
<label for>手机号码:</label>
<input
type="text"
class="txt"
name="mobile"
v-model="user.mobile"
@blur="checkmobile"
/>
<p>请输入手机号码</p>
<p v-if="userMsg.mobile.code==1" class="success">{{userMsg.mobile.message}}</p>
<p v-else class="error">{{userMsg.mobile.message}}</p>
</li>
<li class="checkcode">
<label for>验证码:</label>
<input type="text" name="checkcode" v-model="user.code" />
<!-- prevent阻止事件冒泡行为 -->
<button :disabled="btnDisabled" @click.prevent="sendSms">
发送验证码
<span v-show="btnDisabled">{{senconds}}秒</span>
</button>
<span :class="userMsg.smsData.code==1?'success':'error'">{{userMsg.smsData.message}}</span>
</li>
<li>
<label for> </label>
<input type="checkbox" class="chb" checked="checked" /> 我已阅读并同意《用户注册协议》
</li>
<li>
<label for> </label>
<input type="submit" value @click.prevent="register" class="login_btn" />
</li>
</ul>
</form>
</div>
<div class="mobile fl">
<h3>手机快速注册</h3>
<p>
中国大陆手机用户,编辑短信 “
<strong>XX</strong>”发送到:
</p>
<p>
<strong>1069099988</strong>
</p>
</div>
</div>
</div>
<!-- 注册footer -->
<div style="clear:both;"></div>
<Footer></Footer>
</div>
</template>
<script>
import TopNav from "../components/TopNav";
import HeaderLogo from "../components/HeaderLogo";
import Footer from "../components/Footer";
export default {
head: {
title: "注册页面",
link: [{ rel: "stylesheet", href: "style/login.css" }],
script: [
{ type: "text/javascript", src: "js/header.js" },
{ type: "text/javascript", src: "js/index.js" }
]
},
components: {
TopNav,
HeaderLogo,
Footer
},
data() {
return {
user: {
mobile: "",
p1: "",
p2: ""
},
userMsg: {
//错误提示数据
smsData: "",
username: {},
mobile: {},
password1: {},
password2: {}
},
btnDisabled: false, //倒计时控制变量
senconds: 30, //默认倒计时数
timer: null //接收定时器,清除定时器
};
},
methods: {
//校验用户名
async checkUsername() {
if (this.user.username == null) {
this.userMsg.username = {
code: 0,
message: "用户名不能为空"
};
return;
}
let { data } = await this.$request.checkUsername(this.user.username);
this.userMsg.username = data;
},
//校验手机号
async checkmobile() {
console.info(this.user);
let { data } = await this.$request.checkmobile(this.user);
this.userMsg.mobile = data;
},
//校验密码
checkpassword1() {
if (this.user.p1 == "") {
this.userMsg.password1 = {
code: 0,
message: "密码不能为空"
};
}
},
//校验确认密码
checkpassword2() {
if (this.user.p2 == "") {
this.userMsg.password2 = {
code: 0,
message: "确认密码不能为空"
};
} else if (this.user.p2 != this.user.p1) {
this.userMsg.password2 = {
code: 0,
message: "两次输入密码不一致"
};
}
this.user.password = this.user.p2;
},
async sendSms() {
//简单校验
if (!this.user.username) {
this.userMsg.username = {
code: 0,
message: "用户名不能为空"
};
return;
}
if (!this.user.mobile) {
this.userMsg.mobile = {
code: 0,
message: "手机号不能为空"
};
return;
}
//倒计时
//按钮状态变为不可用
this.btnDisabled = true;
this.timer = setInterval(() => {
//小于1重置
if (this.senconds <= 1) {
//按钮回到可用状态
this.btnDisabled = false;
//关闭定时器
clearInterval(this.timer);
} else {
this.senconds--;
}
}, 1000);
//继续发送
let { data } = await this.$request.sendSms(this.user);
if (data.code == 1) {
//发送成功
alert(data.message);
} else {
//错误信息
this.userMsg.code = data.message;
}
},
async register() {
let { data } = await this.$request.register(this.user);
if (data.code == 1) {
this.$router.push("/login");
} else {
this.userMsg.smsData = data;
}
}
}
};
</script>
<style>
</style>
api.js
//自定义函数
const request = {
//checkUsername
checkUsername: (username) => {
return axios.post('/cgwebservice/user/checkusername', { username })
},
//checkmobile
checkmobile: (user) => {
return axios.post('/cgwebservice/user/checkmobile', user)
},
//发验证码
sendSms: (user) => {
return axios.post('/cgwebservice/sms', user)
},
register: (user) => {
return axios.post('/cgwebservice/user/register', user)
}
}
var axios = null
export default ({ $axios }, inject) => {
//3) 保存内置的axios
axios = $axios
//4) 将自定义函数交于nuxt
// 使用方式1:在vue中,this.$request.xxx()
// 使用方式2:在nuxt的asyncData中,content.app.$request.xxx()
inject('request', request)
}