需求分析
为了方便用户,移动端通常会提供通过手机验证码登录。
手机验证码登录的优点:
- 方便、快捷,无需注册,直接登录。
- 使用短信验证码登录,无需记忆密码。
- 安全。
登录流程:
用户输入手机号->获取验证码->输入验证码->点击登录->登录成功。
注意:通过手机验证码登录,手机号是区分不同用户的标识。
数据模型
- 涉及到user表。
代码开发
前端页面和服务端交互过程
- 在登录页面输入手机号,点击获取验证码按钮,在服务端调用短信服务API向指定手机号用户发送验证码。
- 在登录页面输入验证码,点击登录,服务端处理登陆请求。
搭建好类和接口的基本结构
dao,entity,servic。搭建架子。
1、修改登录过滤器
// 定义所有不处理的请求路径。
String[] urls =
new String[] {
"/backend/**", "/front/**", "/employee/login", "/employee/logout", "/common/**","/user/login","/user/sendMsg"
};
// 3.判断登录状态,
// 后台员工登录- 如果已登录,就直接放行
if (httpServletRequest.getSession().getAttribute("employee") != null) {
log.info("用户已登录,登录的id为: {}", httpServletRequest.getSession().getAttribute("employee"));
// 把id保存到当前ThreadLocal线程中。
Long empId = (Long) httpServletRequest.getSession().getAttribute("employee");
BaseContext.setCurrentId(empId);
// 放行。
chain.doFilter(httpServletRequest, httpServletResponse);
return;
}
// 用户登录- 如果已登录,就直接放行
if (httpServletRequest.getSession().getAttribute("user") != null) {
log.info("用户已登录,登录的id为: {}", httpServletRequest.getSession().getAttribute("user"));
// 把id保存到当前ThreadLocal线程中。
Long userId = (Long) httpServletRequest.getSession().getAttribute("user");
BaseContext.setCurrentId(userId);
// 放行。
chain.doFilter(httpServletRequest, httpServletResponse);
return;
}
发送验证码短信
注意:
由于没有完成阿里云短信服务的套餐订购,所以验证码的一系列功能就取消了,前端随机生成一个验证码,服务端也无需做验证码的校验了。
/**
* 用户登录发送验证码
*
* @param user
* @param session
* @return
*/
@PostMapping("/sendMsg")
public R<String> sendMsg(@RequestBody User user, HttpSession session) {
TODO: 2022/5/6 验证码短信服务待开发,问题:前端没有发送请求,没有注册阿里云短信套餐。
// 获取要登录的手机号。
String phone = user.getPhone();
// 生成随机验证码。
String code = ValidateCodeUtils.generateValidateCode(4).toString();
// 通过阿里云api向用户发送短信。
SMSUtils.sendMessage("", "", phone, code);
// 把验证码存入session域中。
session.setAttribute(phone, code);
return null;
}
移动端客户登录
- 前端传来的json数据有number和code,但是user实体类没有code,所以需要想办法接收参数,可以接收前端参数:形参用Map,或者也可以使用新建dto形式。
/**
* 移动端客户登录
* @param map
* @param session
* @return
*/
@PostMapping("/login")
public R<User> login(@RequestBody Map map, HttpSession session) {
log.info(map.toString());
// 取出电话号。
String phone = map.get("phone").toString();
// 取出map中的验证码。
// 判断验证码是否与sesstion中的验证码相等,
// 如果相等,则登录成功。
// 进一步判断手机号是否为新用户,
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(User::getPhone, phone);
User user = userService.getOne(queryWrapper);
// 如果为新用户则自动为此新用户进行注册。
if (user == null) {
User user1 = new User();
user1.setPhone(phone);
userService.save(user1);
//把id存入session域中,方便过滤器筛选。
session.setAttribute("user",user1.getId());
return R.success(user1);
}
// 如果不相等返回登录失败信息
return R.success(user);
}
功能测试
测试通过。
优化
前面已经实现了手机端验证码登录,随机生成的验证码保存在httpsession中,我们需要改造把验证码保存到redis中。
具体思路:
- 把RedisTemplate对象注入UserController中,
- 在sendMsg方法中把验证码存入redis中,并设置有效期为5分钟。
- 在login方法中,获取redis中的验证码,如果登录成功则删除redis中验证码。
/**
* 用户登录发送验证码
*
* @param user
* @param session
* @return
*/
@PostMapping("/sendMsg")
public R<String> sendMsg(@RequestBody User user, HttpSession session) {
TODO: 2022/5/6 验证码短信服务待开发,问题:前端没有发送请求,没有注册阿里云短信套餐。
// 获取要登录的手机号。
String phone = user.getPhone();
// 生成随机验证码。
String code = ValidateCodeUtils.generateValidateCode(4).toString();
// 通过阿里云api向用户发送短信。
SMSUtils.sendMessage("", "", phone, code);
// 把验证码存入session域中。
// session.setAttribute(phone, code);
//把验证码存入redis中。
redisTemplate.opsForValue().set(phone,code,5L, TimeUnit.MINUTES);
return null;
}
/**
* 移动端客户登录
*
* @param map
* @param session
* @return
*/
@PostMapping("/login")
public R<User> login(@RequestBody Map map, HttpSession session) {
log.info(map.toString());
// 取出电话号。
String phone = map.get("phone").toString();
// 取出map中的验证码。
// 判断验证码是否与sesstion中的验证码相等,
// 如果相等,则登录成功。
// 进一步判断手机号是否为新用户,
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(User::getPhone, phone);
User user = userService.getOne(queryWrapper);
session.setAttribute("user", user.getId());
// 如果为新用户则自动为此新用户进行注册。
if (user == null) {
User user1 = new User();
user1.setPhone(phone);
userService.save(user1);
// 把id存入session域中,方便过滤器筛选。
session.setAttribute("user", user1.getId());
//如果成功就删除验证码。
redisTemplate.delete(phone);
return R.success(user1);
}
//从redis中获取验证码。
String code = (String) redisTemplate.opsForValue().get(phone);
// 如果不相等返回登录失败信息
return R.error("登录失败");
}
前面已经实现了手机端验证码登录,随机生成的验证码保存在httpsession中,我们需要改造把验证码保存到redis中。
具体思路:
- 把RedisTemplate对象注入UserController中,
- 在sendMsg方法中把验证码存入redis中,并设置有效期为5分钟。
- 在login方法中,获取redis中的验证码,如果登录成功则删除redis中验证码。
/**
* 用户登录发送验证码
*
* @param user
* @param session
* @return
*/
@PostMapping("/sendMsg")
public R<String> sendMsg(@RequestBody User user, HttpSession session) {
TODO: 2022/5/6 验证码短信服务待开发,问题:前端没有发送请求,没有注册阿里云短信套餐。
// 获取要登录的手机号。
String phone = user.getPhone();
// 生成随机验证码。
String code = ValidateCodeUtils.generateValidateCode(4).toString();
// 通过阿里云api向用户发送短信。
SMSUtils.sendMessage("", "", phone, code);
// 把验证码存入session域中。
// session.setAttribute(phone, code);
//把验证码存入redis中。
redisTemplate.opsForValue().set(phone,code,5L, TimeUnit.MINUTES);
return null;
}
/**
* 移动端客户登录
*
* @param map
* @param session
* @return
*/
@PostMapping("/login")
public R<User> login(@RequestBody Map map, HttpSession session) {
log.info(map.toString());
// 取出电话号。
String phone = map.get("phone").toString();
// 取出map中的验证码。
// 判断验证码是否与sesstion中的验证码相等,
// 如果相等,则登录成功。
// 进一步判断手机号是否为新用户,
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(User::getPhone, phone);
User user = userService.getOne(queryWrapper);
session.setAttribute("user", user.getId());
// 如果为新用户则自动为此新用户进行注册。
if (user == null) {
User user1 = new User();
user1.setPhone(phone);
userService.save(user1);
// 把id存入session域中,方便过滤器筛选。
session.setAttribute("user", user1.getId());
//如果成功就删除验证码。
redisTemplate.delete(phone);
return R.success(user1);
}
//从redis中获取验证码。
String code = (String) redisTemplate.opsForValue().get(phone);
// 如果不相等返回登录失败信息
return R.error("登录失败");
}