需求: 手机验证码登录,点击发送验证码,输入验证码,登录;
思路:
- 输入手机号,点击发送验证码按钮,单击事件触发 前端页面校验手机号格式是否正确,如不正确就返回手机号有误,正确就携带手机号发送请求,在发送请求前做一个倒计时效果;
- 后台接收到手机号,生成验证码,并将验证码保存在redis中手机号为key,验证码为value设置一个过期时间,调用发送短信服务传入手机号和验证码;
- 用户接收到验证码,输入验证码,前端同样进行一个校验,校验通过传入后端;
- 后端拿到数据,根据手机号查询验证码,返回结果;
注意事项: 避免出现恶意多次发送验证码,可以对手机号发送的验证码数量进行限制,3小时内最多发送3次验证码;
前端页面:
导入相关的工具js
//获取指定的URL参数值 如:http://localhost/pages/setmeal_detail.html?id=3&name=jack
function getUrlParam(paraName) {
var url = document.location.toString();
//alert(url);
//从?开始剪切
var arrObj = url.split("?");
//如果有值表示有参数
if (arrObj.length > 1) {
//去掉?号以&分隔符在剪切
var arrPara = arrObj[1].split("&");
var arr;
//遍历,拿到在键值对,以=号剪切,得到参数值
for (var i = 0; i < arrPara.length; i++) {
arr = arrPara[i].split("=");
if (arr != null && arr[0] == paraName) {
return arr[1];
}
}
return "";
}
else {
return "";
}
}
//获得当前日期,返回字符串
function getToday() {
var today = new Date();
var year = today.getFullYear();
var month = today.getMonth() + 1;//0表示1月,1表示2月
var day = today.getDate();
return (year + "-" + month + "-" + day);
}
//获得指定日期后指定天数的日期
function getSpecifiedDate(date,days) {
date.setDate(date.getDate() + days);//获取指定天之后的日期
var year = date.getFullYear();
var month = date.getMonth() + 1;
var day = date.getDate();
return (year + "-" + month + "-" + day);
}
/**
* 手机号校验
1--以1为开头;
2--第二位可为3,4,5,7,8,中的任意一位;
3--最后以0-9的9个整数结尾。
*/
function checkTelephone(telephone) {
var reg=/^[1][3,4,5,7,8][0-9]{9}$/;
if (!reg.test(telephone)) {
return false;
} else {
return true;
}
}
/**
* 身份证号码校验
* 身份证号码为15位或者18位,15位时全为数字,18位前17位为数字,最后一位是校验位,可能为数字或字符X
*/
function checkIdCard(idCard){
var reg = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/;
if(reg.test(idCard)){
return true;
}else{
return false;
}
}
var clock = '';//定时器对象,用于页面30秒倒计时效果
var nums = 30;
var validateCodeButton;//按钮对象
//基于定时器实现30秒倒计时效果
function doLoop() {
validateCodeButton.disabled = true;//将按钮置为不可点击
nums--;
if (nums > 0) {
validateCodeButton.value = nums + '秒后重新获取';
} else {
clearInterval(clock); //清除js定时器
validateCodeButton.disabled = false;//按钮又可以点击了
validateCodeButton.value = '重新获取验证码';
nums = 30; //重置时间
}
}
以下才是登录页面
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0,user-scalable=no,minimal-ui">
<meta name="description" content="">
<meta name="author" content="">
<link rel="icon" href="../img/asset-favico.ico">
<title>登录</title>
<link rel="stylesheet" href="../css/page-health-login.css" />
<link rel="stylesheet" href="../plugins/elementui/index.css" />
<script src="../plugins/jquery/dist/jquery.min.js"></script>
<script src="../plugins/healthmobile.js"></script>
<script src="../plugins/vue/vue.js"></script>
<script src="../plugins/vue/axios-0.18.0.js"></script>
<script src="../plugins/elementui/index.js"></script>
</head>
<body data-spy="scroll" data-target="#myNavbar" data-offset="150">
<div class="app" id="app">
<!-- 页面头部 -->
<div class="top-header">
<span class="f-left"><i class="icon-back"></i></span>
<span class="center">健康生活</span>
<span class="f-right"><i class="icon-more"></i></span>
</div>
<div style="margin-left: 20px">手机快速登录</div>
<!-- 页面内容 -->
<div class="contentBox">
<div class="login">
<form id='login-form'>
<div class="input-row">
<label>手机号</label>
<div class="loginInput">
<input v-model="loginInfo.telephone" id='account' type="text" placeholder="请输入手机号">
<input id="validateCodeButton" @click="sendValidateCode()" type="button" style="font-size: 12px" value="获取验证码">
</div>
</div>
<div class="input-row">
<label>验证码</label>
<div class="loginInput">
<input v-model="loginInfo.validateCode" style="width:80%" id='password' type="text" placeholder="请输入验证码">
</div>
</div>
<div class="input-row" style="font-size: 12px">
<input type="checkbox" checked>
阅读并同意《健康生活用户协议》《健康生活隐私条款》
</div>
<div class="btn yes-btn"><a @click="login()" href="#">登录</a></div>
</form>
</div>
</div>
</div>
</body>
<script>
var vue = new Vue({
el:'#app',
data:{
loginInfo:{}//登录信息
},
methods:{
//发送验证码
sendValidateCode(){
//获取用户输入的手机号
var telephone = this.loginInfo.telephone;
//调用js方法,校验输入的手机号是否合法
if(!checkTelephone(telephone)){
//校验不通过,提示错误信息
this.$message.error("请输入正确的手机号");
return false;
}
//在按钮上显示30秒倒计时效果
validateCodeButton = $("#validateCodeButton")[0];//锁定dom对象
clock = window.setInterval(doLoop,1000);//定时器方法,可以实现每隔指定的时间调用指定的方法
//发送ajax请求,为用户发送手机验证码
axios.post("/validateCode/send4Login.do?telephone=" + telephone).then((res) => {
if(!res.data.flag){
//短信验证码发送失败
this.$message.error(res.data.message);
}
});
},
//登录
login(){
//为了防止在输入验证码后手机号更换了或者删除了,发送无效请求,所以这里还要再对手机号进行校验
//获取用户输入的手机号
var telephone = this.loginInfo.telephone;
//调用js方法,校验输入的手机号是否合法
if(!checkTelephone(telephone)){
//校验不通过,提示错误信息
this.$message.error("请输入正确的手机号");
return false;
}
//发送ajax请求,将表单数据提交到Controller进行登录处理
axios.post("/member/login.do",this.loginInfo).then((res) => {
if(res.data.flag){
//登录成功,跳转到会员首页
window.location.href = "member.html";
}else{
this.$message.error(res.data.message);
}
});
}
}
});
</script>
</html>
后端 发送短信服务详情见
发送验证码
/**
* 验证码操作
*/
@RestController
@RequestMapping("/validateCode")
public class ValidateCodeController {
@Autowired
private JedisPool jedisPool;
//用户在线预约发送验证码
@RequestMapping("/send4Order")
public Result send4Order(String telephone){
//首先调用方法随机生成4位数字验证码
Integer validateCode = ValidateCodeUtils.generateValidateCode(4);
//给用户发送验证码
try{
//发送短信可能会出现异常,使用try catch
SMSUtils.sendShortMessage(SMSUtils.VALIDATE_CODE,telephone,validateCode.toString());
}catch (Exception e){
e.printStackTrace();
return new Result(false, MessageConstant.SEND_VALIDATECODE_FAIL);
}
//将验证码保存到redis(有效期5分钟)
jedisPool.getResource().setex(telephone + RedisMessageConstant.SENDTYPE_ORDER,300,validateCode.toString());
return new Result(true,MessageConstant.SEND_VALIDATECODE_SUCCESS);
}
//用户手机快速登录发送验证码
@RequestMapping("/send4Login")
public Result send4Login(String telephone){
//随机生成6位数字验证码
Integer validateCode = ValidateCodeUtils.generateValidateCode(6);
//给用户发送验证码
try{
SMSUtils.sendShortMessage(SMSUtils.VALIDATE_CODE,telephone,validateCode.toString());
}catch (Exception e){
e.printStackTrace();
return new Result(false, MessageConstant.SEND_VALIDATECODE_FAIL);
}
//将验证码保存到redis(5分钟)
jedisPool.getResource().setex(telephone + RedisMessageConstant.SENDTYPE_LOGIN,300,validateCode.toString());
return new Result(true,MessageConstant.SEND_VALIDATECODE_SUCCESS);
}
}
手机号码登录
@RestController
@RequestMapping("/member")
public class MemberController {
@Autowired
private JedisPool jedisPool;
@Reference
private MemberService memberService;
//手机号快速登录
@RequestMapping("/login")
public Result login(HttpServletResponse response, @RequestBody Map map){
String telephone = (String) map.get("telephone");//获取手机号码
String validateCode = (String) map.get("validateCode");//获取验证码
//从Redis中获取保存的验证码
String validateCodeInRedis = jedisPool.getResource().get(telephone + RedisMessageConstant.SENDTYPE_LOGIN);
//验证码输入正确
if(validateCodeInRedis != null && validateCode != null && validateCode.equals(validateCodeInRedis)){
//判断当前用户是否为会员(查询会员表来确定)
Member member = memberService.findByTelephone(telephone);
if(member == null){
//不是会员,自动完成注册(自动将当前用户信息保存到会员表)
member.setRegTime(new Date());
member.setPhoneNumber(telephone);
memberService.add(member);
}
//向客户端浏览器写入Cookie,内容为手机号
Cookie cookie = new Cookie("login_member_telephone",telephone);
cookie.setPath("/");
cookie.setMaxAge(60*60*24*30);
response.addCookie(cookie);
//将会员信息保存到Redis
String json = JSON.toJSON(member).toString();
jedisPool.getResource().setex(telephone,60*30,json);
return new Result(true,MessageConstant.LOGIN_SUCCESS);
}else{
//验证码输入错误
return new Result(false, MessageConstant.VALIDATECODE_ERROR);
}
}
}