• Reggie外卖项目 —— 移动端小程序之手机验证码登录


    23、手机验证码登录

    23.1、需求分析

    1、为了方便用户登录,移动端通常都会提供通过手机验证码登录的功能。

    2、手机验证码登录的优点:

    • 方便快捷,无需注册,直接登录
    • 使用短信验证码作为登录凭证,无需记忆密码
    • 安全

    3、登录流程:

    输入手机号码>获取验证码>输入验证码>点击登录>登录成功

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

    23.2、数据模型

    通过手机验证码登录时,涉及的表为user表,即用户表。结构如下:

    在这里插入图片描述

    23.3、代码开发

    23.3.1、梳理交互过程

    在开发代码之前,需要梳理一下登录时前端页面和服务端的交互过程:

    1、在登录页面(front/page/login.html)输入手机号,点击【获取验证码】按钮,页面发送ajax请求,在服务端调用短信服务API给指定的手机号发送验证码短信。

    2、在登录页面输入验证码,点击【登录】按钮,发送ajax请求,在服务端处理登录请求

    开发手机验证码登录功能,其实就是在服务端编写代码去处理前端页面发送的这2次请求即可。

    23.3.2、准备工作

    在开发业务功能前,先将需要用到的类和接口基本结构创建好:

    • 实体类User

      /**
       * 用户信息
       */
      @Data
      public class User implements Serializable {
          private static final long serialVersionUID = 1L;
      
          private Long id;
      
          //姓名
          private String name;
      
          //手机号
          private String phone;
      
          //性别 0 女 1 男
          private String sex;
      
          //身份证号
          private String idNumber;
      
          //头像
          private String avatar;
      
          //状态 0:禁用,1:正常
          private Integer status;
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
    • Mapper接口UserMapper

      @Mapper
      public interface UserMapper extends BaseMapper<User> {
      }
      
      • 1
      • 2
      • 3
    • 业务层接口Userservice

      public interface UserService extends IService<User> {
      }
      
      • 1
      • 2
    • 业务层实现类UserServiceImpl

      @Service
      public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
      }
      
      • 1
      • 2
      • 3
    • 控制层UserController

      @RestController
      @RequestMapping("/user")
      @Slf4j
      public class UserController {
      
          @Autowired
          private UserService userService;
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
    • 工具类SMSUtilsvalidateCodeUtils

      SMSUtils.java

      /**
       * 短信发送工具类
       */
      public class SMSUtils {
      
         /**
          * 发送短信
          * @param signName 签名
          * @param templateCode 模板
          * @param phoneNumbers 手机号
          * @param param 参数
          */
         public static void sendMessage(String signName, String templateCode,String phoneNumbers,String param){
            DefaultProfile profile = DefaultProfile.getProfile("cn-hangzhou", "", "");
            IAcsClient client = new DefaultAcsClient(profile);
      
            SendSmsRequest request = new SendSmsRequest();
            request.setSysRegionId("cn-hangzhou");
            request.setPhoneNumbers(phoneNumbers);
            request.setSignName(signName);
            request.setTemplateCode(templateCode);
            request.setTemplateParam("{\"code\":\""+param+"\"}");
            try {
               SendSmsResponse response = client.getAcsResponse(request);
               System.out.println("短信发送成功");
            }catch (ClientException e) {
               e.printStackTrace();
            }
         }
      
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31

      validateCodeUtils.java

      /**
       * 随机生成验证码工具类
       */
      public class ValidateCodeUtils {
          /**
           * 随机生成验证码
           * @param length 长度为4位或者6位
           * @return
           */
          public static Integer generateValidateCode(int length){
              Integer code =null;
              if(length == 4){
                  code = new Random().nextInt(9999);//生成随机数,最大为9999
                  if(code < 1000){
                      code = code + 1000;//保证随机数为4位数字
                  }
              }else if(length == 6){
                  code = new Random().nextInt(999999);//生成随机数,最大为999999
                  if(code < 100000){
                      code = code + 100000;//保证随机数为6位数字
                  }
              }else{
                  throw new RuntimeException("只能生成4位或6位数字验证码");
              }
              return code;
          }
      
          /**
           * 随机生成指定长度字符串验证码
           * @param length 长度
           * @return
           */
          public static String generateValidateCode4String(int length){
              Random rdm = new Random();
              String hash1 = Integer.toHexString(rdm.nextInt());
              String capstr = hash1.substring(0, length);
              return capstr;
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39

    23.3.3、修改LoginCheckFilter

    1、前面我们已经完成了LoginCheckFilter过滤器的开发,此过滤器用于检查用户的登录状态。我们在进行手机验证码登录时,发送的请求需要在此过滤器处理时直接放行。

    //定义不需要处理的请求路径
    String[] urls = new String[]{
            "/employee/login",
            "/employee/logout",
            "/backend/**",
            "/front/**",
            "/common/**",
            "/user/sendMsg",//移动端发送短信
            "/user/login" //移动端登录
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    2、在LoginCheckFilter过滤器中扩展逻辑,判断移动端用户登录状态:

    //4-2、判断登录状态,如果已登录,则直接放行
    if (request.getSession().getAttribute("user") != null) {
        log.info("用户已登录,用户id为:{}", request.getSession().getAttribute("user"));
    
        Long userId = (Long) request.getSession().getAttribute("user");
        BaseContext.setCurrentId(userId);
    
        filterChain.doFilter(request, response);
        return;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    23.4、功能测试

    23.4.1、发送手机验证码

    1、前端开发

    在这里插入图片描述

    2、Controller开发

    /**
     * 发送手机验证码
     * @param user
     * @return
     */
    @PostMapping("/sendMsg")
    public R<String> sendMsg(@RequestBody User user, HttpSession session) {
        //获取手机号
        String phone = user.getPhone();
    
        if (StringUtils.isNotEmpty(phone)) {
            //生成随机的4位验证码
            String code = ValidateCodeUtils.generateValidateCode(4).toString();
            log.info("code={}", code);
    
            //调用阿里云提供的短信服务API完成发送短信
            //SMSUtils.sendMessage("瑞吉外卖", "", phone, code);
    
            //需要将生成的验证码保存到Session
            session.setAttribute(phone, code);
            return R.success("手机验证码短信发送成功");
        }
        return R.error("手机验证码短信发送失败");
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    23.4.2、移动端登录

    1、前端开发

    在这里插入图片描述

    2、Controller开发

    /**
     * 移动端用户登录
     * @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();
    
        //获取验证码
        String code = map.get("code").toString();
    
        //从Session获取保存的验证码
        String codeInSession = session.getAttribute(phone).toString();
    
        //进行验证码的比对(页面提交的验证码和Session中保存的验证码比对)
        if (codeInSession != null && codeInSession.equals(code)) {
            //如果比对成功,说明登录成功
            LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(User::getPhone, phone);
    
            User user = userService.getOne(queryWrapper);
            if (user == null) {
                //判断当前手机号对应的用户是否为新用户,如果是新用户就自动完成注册
                user = new User();
                user.setPhone(phone);
                user.setStatus(1);
                userService.save(user);
            }
            session.setAttribute("user",user);
            return R.success(user);
        }
    
        return R.error("登录失败");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
  • 相关阅读:
    NTP时间同步服务器设置
    LeetCode_1049_最后一块石头的重量Ⅱ
    【Sensor有点意思】之RCCB传感器
    golang pprof
    为什么消费返利模式层出不穷?这个消费返利玩法值得你借鉴
    【Rust中的struct】
    LeetCode 2385.感染二叉树需要的总时间:两次搜索(深搜 + 广搜)
    什么是深度学习?最易懂的机器学习入门文章
    创建最基本的web服务器-http模块
    NR CSI(三) CQI
  • 原文地址:https://blog.csdn.net/kuaixiao0217/article/details/127738923