社区/文章分享/如何用云开发高效快捷地实现短信验证码登录?

如何用云开发高效快捷地实现短信验证码登录?

云开发扩展能力之短信验证码登录

短信验证码登录支持在客户端使用短信验证码进行登录,封装了短信验证码的生成、发送、校验和定时清理逻辑,帮助开发者进行鉴权。登录成功后,用户身份将转化为正式用户。

本次实战我们将使用 web 云开发实现短信验证码登录并新增数据。

资源准备

  • 云开发环境
  • 腾讯云短信服务
  • 短信验证码登录扩展能力
  • 静态网站托管

一、云开发环境开通

如果已有云环境,可直接跳过这一步

打开云开发控制台新建云环境:

创建云开发环境

打开云开发 CloudBase 控制台,打开左侧菜单里的数据库,点击添加集合,新增一个test集合用于新增数据测试。

添加集合

二、腾讯云短信服务

  1. 开通短信服务

登录腾讯云短信控制台,这里的账号不限于小程序的账号,其他账号也可以;也不限于是个人账号还是企业账号,不过账号需要进行实名认证,个人认证用户只能发送短信验证码、短信通知等,不能用于营销短信;企业认证用户可以发送短信验证码、短信通知、营销短信等。如果账号已经认证,直接申请短信服务就可以开通了。

  1. 创建签名和正文模板

国内短信由签名+正文组成,签名符号为【】,发送短信内容时必须带签名。所以要发送短信,需要申请短信签名和正文模板,两者都通过审核后,就可以开始发送短信了。

创建签名

打开左侧菜单里的国内短信-签名管理,点击创建签名,创建完签名后,这个签名内容之后会用到。

  • 签名用途:选择【自用(签名为本账号实名认证的公司、网站、产品名等)】。
  • 签名类型:选择【公众号】。
  • 签名内容:输入公司名或小程序名或公众号名或产品名称
  • 证明类型:选择公众号设置页面截图,然后上传小程序设置页面截图,可以参考案例;

创建签名

创建正文模板

打开左侧菜单里的国内短信-正文模板管理,点击创建正文模板,创建完模板后,会有一个模板ID,这个之后会用到,也要记住你模板的变量位置。

  • 模板名称,建议带有明确目的的名称,比如“注册通知”、“购买成功反馈”等;
  • 短信类型:选择【普通短信】
  • 短信内容:比如“您正在申请手机注册,验证码为:{1},{2}分钟内有效!”,这里的{1}{2}是你要在代码里传入的变量,变量的编码必须是从{1}开始,传入变量时也要按照顺序传入

创建正文模板

三、短信验证码登录扩展能力

打开云开发 CloudBase 控制台,打开左侧菜单里的扩展能力,点击短信验证码登录模块进行安装。

安装短信登录扩展

该扩展会创建以下云资源:

云函数

  • tcb-sms-auth
    生成校验码并发送到指定手机号,以实现短信验证码登录

云数据库

  • tcb-sms-auth
    存储验证码相关信息。

根据提示完成配置和安装。

四、开通静态网站托管

云开发为开发者提供静态网页托管的能力,静态资源(HTML、CSS、JavaScript、字体等)的分发由对象存储 COS 和拥有多个边缘网点的 CDN 提供支持。您可在腾讯云控制台进行静态网站的部署,提供给您的用户访问。

打开云开发 CloudBase 控制台,打开左侧菜单里的静态网站托管,点击开启使用,然后等待几分钟便可初始化完成。

静态网站托管

使用扩展

在 web 网站使用该扩展,请先在 云开发控制台 将网站域名添加为当前环境的安全域名。

打开云开发 CloudBase 控制台,复制静态网站托管-基础配置里的默认域名,然后打开左侧菜单里的环境-安全配置,将默认域名添加为WEB 安全域名

添加web安全域名

一、使用方法

  1. 安装扩展 SDK 到项目
  • 方法一:通过包管理器引入
npm install --save @cloudbase/extension-sms
  • 方法二:通过 CDN 引入
<script src="//unpkg.com/@cloudbase/extension-sms/built/index.umd.js"></script>
  1. 调用扩展 SDK

调用参数

名称 类型 是否必须 说明
action String 操作类型,支持 Send 和 Login
phone String 电话号码
app Tcb tcb 实例
smsCode String 短信验证码,action 为 Login 时需要传入
customDomain String HTTP 触发的自定义域名

返回内容

目前两种 action 都没有返回值。(本例采用 auth.hasLoginState() 来判断登录 )

二、编写代码

写了一个简单的 demo 来实现登录并新增数据功能,代码如下:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="Pragma" content="no-cache" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1.0, user-scalable=no"
    />
    <meta name="format-detection" content="telephone=yes" />
    <meta name="apple-mobile-web-app-capable" content="yes" />
    <meta name="apple-mobile-web-app-status-bar-style" content="black" />
    <title>腾讯云云开发短信验证码登录</title>
    <!-- Bootstrap core CSS-->
    <!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
    <link
      rel="stylesheet"
      href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"
    />

    <style type="text/css">
      body {
        margin: 0;
        padding: 0;
      }

      .modal_content {
        padding: 30px;
        display: flex;
        justify-content: center;
        flex-direction: column;
      }

      .modal_content > div {
        margin-bottom: 20px;
      }

      .modal_content > h5:first-child {
        margin: 30px 0px;
      }

      #dialog label {
        color: #666;
      }

      #phone1 {
        display: block;
        width: 100%;
        height: 70px;
        background: none;
        padding-top: 30px;
        border: 0;
        outline: none;
        text-align: center;
        margin-top: -30px;
        font-size: 16px;
        border-bottom: 1px solid rgba(0, 0, 0, 0.2);
        border-radius: 0;
      }

      .code1 {
        display: flex;
        flex-direction: row;
        justify-content: space-between;
        width: 100%;
        height: 70px;
        background: none;
        padding-top: 30px;
        margin-top: -30px;
        font-size: 16px;
        border-bottom: 1px solid rgba(0, 0, 0, 0.2);
        border-radius: 0;
      }

      #code1 {
        width: calc(100% - 90px);
        height: 55px;
        background: none;
        padding-top: 20px;
        border: 0;
        outline: none;
        text-align: center;
        margin-top: -20px;
        font-size: 16px;
      }

      #btnSendCode1 {
        width: 90px;
        height: 30px;
        padding: 0 5px;
        margin: 0;
        font-size: 14px;
        text-align: center;
        background: transparent;
        border-radius: 30px;
        color: #a07941;
        border-color: #a07941;
      }

      ::-webkit-input-placeholder {
        /* WebKit browsers */
        font-size: 14px;
        color: rgba(0, 0, 0, 0.4);
      }

      :-moz-placeholder {
        /* Mozilla Firefox 4 to 18 */
        font-size: 14px;
        color: rgba(0, 0, 0, 0.4);
      }

      ::-moz-placeholder {
        /* Mozilla Firefox 19+ */
        font-size: 14px;
        color: rgba(0, 0, 0, 0.4);
      }

      :-ms-input-placeholder {
        /* Internet Explorer 10+ */
        font-size: 14px;
        color: rgba(0, 0, 0, 0.4);
      }

      .next {
        text-align: center;
        margin: 20px 0;
      }

      .next button {
        width: 100%;
        height: 45px;
        padding: 0;
        margin: 0;
        background: #007bff;
        color: #fff;
        border: 0;
        outline: none;
        border-radius: 3px;
      }

      .content2 {
        display: none;
      }
    </style>
  </head>
  <body>
    <div class="modal_content content1">
      <h5>用户登录</h5>
      <div>
        <label for="phone1">手机号:</label><br />
        <input
          id="phone1"
          type="text"
          autocomplete="off"
          placeholder="请输入手机号"
        />
      </div>
      <div>
        <label for="code1">验证码:</label>
        <div class="code1">
          <input
            id="code1"
            type="text"
            autocomplete="off"
            placeholder="短信验证码"
          />
          <input
            id="btnSendCode1"
            type="button"
            class="btn btn-default"
            value="获取验证码"
            onClick="sendMessage1()"
          />
        </div>
      </div>
      <div class="next">
        <button onClick="binding()">确定</button>
      </div>
    </div>

    <div class="modal_content content2">
      <h5>新增数据</h5>
      <div>
        <label for="text1">文本:</label><br />
        <input id="text1" type="text" placeholder="请输入新增的数据" />
      </div>
      <div class="next">
        <button onClick="add()">确定</button>
      </div>
    </div>

    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/1.9.1/jquery.js"></script>
    <script src="//imgcache.qq.com/qcloud/tcbjs/1.7.1/tcb.js"></script>
    <script src="//unpkg.com/@cloudbase/extension-sms/built/index.umd.js"></script>
    <script>
      const app = tcb.init({
        env: "env-jftmiosg",
      });
      const auth = app.auth();
      var db = app.database();
      tcb.registerExtension(window.extSms);
      islogin();
      var phoneReg = /(^1[3|4|5|7|8]\d{9}$)|(^09\d{8}$)/; //手机号正则
      var count = 60; //间隔函数,1秒执行
      var InterValObj1; //timer变量,控制时间
      var curCount1; //当前剩余秒数

      function islogin() {
        if (auth.hasLoginState()) {
          // 此时已经登录
          $(".content1").hide();
          $(".content2").show();
          return true;
        }
        return false;
      }

      /*第一*/
      async function sendMessage1() {
        curCount1 = count;
        var phone = $.trim($("#phone1").val());
        if (!phoneReg.test(phone)) {
          alert(" 请输入有效的手机号码");
          return false;
        }
        //设置button效果,开始计时
        $("#btnSendCode1").attr("disabled", "true");
        $("#btnSendCode1").val(+curCount1 + "秒再获取");
        InterValObj1 = window.setInterval(SetRemainTime1, 1000); //启动计时器,1秒执行一次
        //向后台发送处理数据
        try {
          await tcb.invokeExtension(extSms.name, {
            action: "Send",
            app,
            phone,
          });
        } catch (err) {
          console.log(JSON.stringify(err, null, 4));
        }
      }

      function SetRemainTime1() {
        if (curCount1 == 0) {
          window.clearInterval(InterValObj1); //停止计时器
          $("#btnSendCode1").removeAttr("disabled"); //启用按钮
          $("#btnSendCode1").val("重新发送");
        } else {
          curCount1--;
          $("#btnSendCode1").val(+curCount1 + "秒再获取");
        }
      }

      /*提交*/
      async function binding() {
        var phone = $.trim($("#phone1").val());
        var smsCode = $.trim($("#code1").val());
        try {
          await tcb.invokeExtension(extSms.name, {
            action: "Login",
            app,
            phone,
            smsCode,
          });
        } catch (err) {
          console.log(JSON.stringify(err, null, 4));
        } finally {
          //无论try catch结果如何还是继续执行
          if (islogin()) {
            alert("登录成功,目前是正式用户");
          } else {
            alert("登录失败,请检查验证码");
          }
        }
      }

      async function add() {
        try {
          var text = $.trim($("#text1").val());
          await db
            .collection("test")
            .add({
              text: text,
            })
            .then((res) => {
              alert("数据新增成功");
              console.log(res);
            })
            .catch((e) => {
              alert("数据新增失败");
              console.log(e);
            });
        } catch (err) {
          console.log(JSON.stringify(err, null, 4));
        }
      }
    </script>
  </body>
</html>

三、上传部署

完成 demo 编写后,将代码上传至静态网站托管,完成部署。

  • 控制台上传

打开云开发 CloudBase 控制台,打开左侧菜单里的静态网站托管,将刚刚编写的文件sms.html上传。

上传文件

  • CLI 工具上传

CloudBase CLI 是一个开源的命令行界面交互工具,用于帮助用户快速、方便的部署项目,管理云开发资源。具体安装步骤和使用方法可以访问云开发 CLI 工具文档进行查看。

效果展示

  • 发送短信

发送短信

  • 登录验证

登录验证

  • 新增数据

新增数据

总结

通过上述步骤,利用云开发提供的短信验证码登录扩展能力,配合云开发的 SDK 在 web 端很方便的实现了自定义登录并新增数据,大家快去试试吧!