需求分析

为了方便用户,移动端通常会提供通过手机验证码登录。
手机验证码登录的优点:

  • 方便、快捷,无需注册,直接登录。
  • 使用短信验证码登录,无需记忆密码。
  • 安全。
    登录流程:
    用户输入手机号->获取验证码->输入验证码->点击登录->登录成功。

注意:通过手机验证码登录,手机号是区分不同用户的标识。

数据模型

  • 涉及到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("登录失败");
  }