前言
随着手机号码实名制以来,手机验证码登录已经算是大趋势了,其优点一是不用伤脑筋去记什么账号,直接输入自己手机号码就行了,省事;其二是便捷,只需要通过手机接收的短信验证码即可完成登录,整个流程给用户体验非常好,很流畅;其三是处理事件时更迅速,比如账号出现异常情况,可以第一时间通过短信方式提示用户该如何应对。还有好多利于用户的地方,这里不一一举例了,进入主题,撸代码~
梳理流程
接入短信SDK这里不做说明,默认已经接入成功,可调用发送短信
和大部分手机验证码登录一样,大体流程如下:
- 用户输入手机号码,点击获取验证码,调用后台获取验证码接口,后端接收用户手机号,生成验证码并返回
- 用户接收到验证码后点击登录,调用手机验证码登录接口,后台接收用户手机号和验证码,根据校验结果返回对应数据或状态
正文
第一步,后端接收用户输入的手机号码,生成验证码,发送短信验证码至用户手机
说明:
- 为了做到灵活配置性,通过参数seconds支持前端定义验证码有效时长,如果前端未设置则取默认值1分钟
- 通过手机号生成缓存key,存储短信验证码并设置生效时间,便于后续用户发起登录时做校验
代码如下:
/**
* 发送登录验证码
* @param mobile 手机号
* @param seconds 有效时长(单位: 秒)
* @return 验证码
*/
public R sendCaptcha(String mobile, Integer seconds){
Redis redis= RedisManager.getRedis();
String key = BaseConstant.PHONE_LOGIN_KEY + mobile;
//默认有效时长60秒
Integer effTime = 60;
if (redis.exists(key)){
return R.error("验证码仍在有效期");
}
if(StringUtils.isEmpty(mobile)){
return R.error("参数mobile不能为空");
}
if(seconds != null){
effTime = seconds;
}
String captcha = "";
try{
//查询短信余额
Integer smsNum = (Integer) (queryBal().get("data"));
if(smsNum == 0){
return R.error("短信余额不足,请联系平台续费!");
}
StringBuffer sb = new StringBuffer();
//生成六位随机数
captcha = String.valueOf((int)((Math.random() * 9 + 1 ) * 100000));
String content = "您的登录验证码为: " + captcha + ",有效时长" + effTime/60 + "分钟,请尽快完成登录,如非本人操作请忽略此短信!";
sb.append("username=" + SmsConstant.SMS_USERNAME)
.append("&userpwd=" + SmsConstant.SMS_PASSWORD)
.append("&mobiles=" + mobile)
.append("&content=" + URLEncoder.encode(SmsConstant.SMS_SIGN + content, "UTF-8"));
//发送请求
String resultXml = HttpUtil.sendPost(SmsConstant.SEND_URL, sb.toString());
AdminSms adminSms = new AdminSms();
adminSms.setMobile(mobile);
adminSms.setContent(content);
adminSms = parseXml(resultXml, adminSms);
if(adminSms.getErrorcode() == 1){
//存储redis缓存----根据前端传递的有效时长缓存验证码
redis.setex(key, effTime-1, captcha);
//存储短信数据
adminSms.setSendTime(new Date());
adminSms.save();
}
} catch (Exception e) {
Log.getLog(getClass()).error("发送短信验证码异常: ", e);
}
return R.ok().put("data", captcha);
}
第二步,用户接收到验证码后点击登录,调用手机验证码登录接口,后台接收用户手机号和验证码,根据校验结果返回对应数据或状态
说明:
- 通过上一步我们利用手机号码生成的缓存key,捞取验证码,做对应的校验处理,失败则返回提示信息,成功则返回登录凭证及所需的用户数据即可
- 通过缓存中设置的验证码生效时长,控制验证码的可使用时间
代码如下:
/**
* 用户登录----手机验证码登录
* @author Ivan
* @param mobile 手机号
* @param captcha 验证码
*/
public void phoneLogin(@Para("mobile") String mobile, @Para("captcha") String captcha){
AdminUser user = AdminUser.dao.findFirst(Db.getSql("admin.user.queryByMobile"), mobile.trim());
if (user == null) {
renderJson(R.error("该手机号码未注册!"));
return;
}
if (user.getStatus() == 0) {
renderJson(R.error("账户被禁用!"));
return;
}
Redis redis= RedisManager.getRedis();
String key = BaseConstant.PHONE_LOGIN_KEY + mobile;
if (redis.exists(key)) {
String redisRusult = redis.get(key);
if(!redisRusult.equals(captcha)){
renderJson(R.error("验证码输入错误!"));
return;
}
} else {
renderJson(R.error("验证码已失效!"));
return;
}
if (user.getStatus() == 2) {
user.setStatus(1);
}
//获取用户名缓存
String usernameKey = BaseConstant.USER_LOGIN_ERROR_KEY + user.getUsername();
redis.del(usernameKey);
String token = IdUtil.simpleUUID();
user.setLastLoginIp(ServletUtil.getClientIP(getRequest()));
user.setLastLoginTime(new Date());
user.update();
user.setRoles(adminRoleService.queryRoleIdsByUserId(user.getUserId()));
redis.setex(token, 3600, user);
Integer type = getParaToInt("type", 1);
BaseUtil.setToken(user.getUserId(), token, type);
renderJson(R.ok().put("Admin-Token", token).put("user", user).put("auth", adminRoleService.auth(user.getUserId())));
}
结语
通过以上两步(两个接口),已经完成了整个手机验证码登录流程,由于目前的项目用的是Jfinal框架,此处代码也以Jfinal为例,其他框架实际上也是大同小异,主要看的是设计思路。
标题:手机验证码登录 思路及实战
地址:http://blog.ivan.group/article/21