• 用Abp实现两步验证(Two-Factor Authentication,2FA)登录(二):Vue网页端开发


    @


    前端代码的框架采用vue.js + elementUI 这套较为简单的方式实现,以及typescript语法更方便阅读。

    首先添加全局对象:

    loginForm: 登录表单对象
    twoFactorData: 两步验证数据,
    showTwoFactorSuccess: 是否显示两步验证成功提示

    loginForm: {
        //登录对象
        username: "",
        password: "",
        twoFactorAuthenticationToken: "",
        twoFactorAuthenticationProvider: "Phone",
    },
    twoFactorData: null,
    showTwoFactorSuccess: false,
    

    发送验证码

    编写发送验证码函数sendVerificationCode,发送验证码后,启动定时器,60秒后可以再次发送验证码。

       async sendVerificationCode() {
          this.smsSendCd = 60;
          this.timer = setInterval(() => {
            this.smsSendCd--;
            if (this.smsSendCd <= 0) {
              clearInterval(this.timer);
            }
          }, 1000);
          await request(
            `${this.host}api/TokenAuth/SendTwoFactorAuthenticateCaptcha`,
            "post",
            {
              provider: "Phone",
              userId: this.twoFactorData.userId,
            }
          )
            .catch((re) => {
              var res = re.response.data;
              this.errorMessage(res.error.message);
            })
            .then((re) => {
              var res = re.data.result;
              this.showTwoFactorSuccess = true;
              this.showTwoFactorSuccess = false;
              this.successMessage("发送验证码成功");
            });
        },
    

    request 是利用axios库发送带有访问凭证Header请求功能的封装 ,ajaxRequest.ts请参考博文使用 Abp.Zero 搭建第三方登录模块(三):网页端开发

    这里使用js-cookie库获取cookie中的访问凭证,并添加到Header中

    import { request } from "@/ajaxRequire";
    import Cookies from "js-cookie";
    
    const tokenKey = "main_token";
    const setToken = (token: string) => Cookies.set(tokenKey, token);
    const cleanToken = () => Cookies.remove(tokenKey);
    const getToken = () => Cookies.get(tokenKey);
    

    登录

    编写登录函数handleLogin:

     async handleLogin() {
          this.loading = true;
    
          var userNameOrEmailAddress = this.loginForm.username;
          var password = this.loginForm.password;
    
          var twoFactorAuthenticationToken =
            this.loginForm.twoFactorAuthenticationToken;
          var twoFactorAuthenticationProvider =
            this.loginForm.twoFactorAuthenticationProvider;
    
          userNameOrEmailAddress = userNameOrEmailAddress.trim();
          await request(`${this.host}api/TokenAuth/Authenticate`, "post", {
            userNameOrEmailAddress,
            password,
            twoFactorAuthenticationToken,
            twoFactorAuthenticationProvider,
          })
            .catch((re) => {
              var res = re.response.data;
              this.errorMessage(res.error.message);
            })
            .then(async (res) => {
              var data = res.data.result;
              if (data.requiresTwoFactorAuthenticate) {
                this.twoFactorData = data;
              } else {
                setToken(data.accessToken);
                setRememberClientToken(data.rememberClientToken);
                await this.getCurrentUser();
              }
            })
            .finally(() => {
              setTimeout(() => {
                this.loading = false;
              }, 1.5 * 1000);
            });
        },
    

    请注意,当需要进行两步验证时,requiresTwoFactorAuthenticate会返回true,同时返回
    twoFactorAuthenticationProviders。

    退出登录

    登出, 将Token以及用户信息置空

    "loading"
        type="danger"
        style="width: 100%"
        @click.native.prevent="logout">
        退出登录
    
    
    logout() {
        setToken(null);
        this.token = null;
        this.userInfo = null;
    },
    

    界面控件

    在登录表单的HTML中,添加两步验证控件:
    显示规则为,当需要两步验证时(即twoFactorData不为空),显示两步验证控件,否则显示登录控件。

    根据twoFactorAuthenticationProviders。我们采用了两种方式,一种是短信验证码,一种是邮箱验证码,这里我们采用了elementUI的tab组件,来实现两种方式的切换。

         "loginForm"
            :model="loginForm"
            class="login-form"
            autocomplete="on"
            label-position="left"
          >
            <template v-if="twoFactorData == null">
                ...
            template>
            <template v-else>
              <p>您的账号开启了两步验证,请选择一种认证方式以继续登录p>
              <el-tabs
                v-model="loginForm.twoFactorAuthenticationProvider"
                tab-position="top"
              >
                <el-tab-pane
                  :lazy="true"
                  label="SMS短信验证"
                  name="Phone"
                  :disabled="
                    twoFactorData.twoFactorAuthenticationProviders.indexOf(
                      'Email'
                    ) == -1
                  "
                >
                  <el-row>
                    <el-col
                      :span="24"
                      style="
                         {
                          margin-bottom: 10px;
                        }
                      "
                    >
                      <el-alert
                        v-if="showTwoFactorSuccess"
                        title="验证码已发送至用户的手机号,请查收"
                        type="info"
                      >
                      el-alert>
                    el-col>
                    <el-col :span="24">
                      <el-form-item
                        class="item"
                        prop="twoFactorAuthenticationToken"
                      >
                        <el-input
                          v-model="loginForm.twoFactorAuthenticationToken"
                          :placeholder="'发送验证码后键入验证码'"
                          tabindex="2"
                          autocomplete="on"
                          @blur="capsTooltip = false"
                        >
                          <el-button
                            slot="append"
                            :disabled="smsSendCd > 0"
                            @click="sendVerificationCode"
                            >{{
                              smsSendCd == 0 ? "发送验证码" : smsSendCd + "后重试"
                            }}
                        el-input>
                      el-form-item>
                    el-col>
                  el-row>
                el-tab-pane>
    
                <el-tab-pane
                  :lazy="true"
                  label="邮箱验证"
                  name="Email"
                  :disabled="
                    twoFactorData.twoFactorAuthenticationProviders.indexOf(
                      'Email'
                    ) == -1
                  "
                >
                  <el-row>
                    <el-col :span="24">
                      <el-alert
                        v-if="showTwoFactorSuccess"
                        title="验证码已发送至登录用户对应的邮箱,请查收"
                        type="info"
                      >
                      el-alert>
                    el-col>
                    <el-col :span="24">
                    ...
                    el-col>
                  el-row>
                el-tab-pane>
              el-tabs>
            template>
    
            <el-row type="flex" class="row-bg" justify="center" :gutter="10">
              <el-col :span="10" v-if="twoFactorData != null">
                <el-button
                  :loading="loading"
                  style="width: 100%"
                  @click.native.prevent="twoFactorData = null"
                >
                  返回
                el-button>
              el-col>
              <el-col :span="10">
                <el-button
                  :loading="loading"
                  type="primary"
                  style="width: 100%"
                  @click.native.prevent="handleLogin"
                >
                  {{ twoFactorData == null ? "登录" : "继续" }}
                el-button>
              el-col>
            el-row>
          el-form>
    

    在这里插入图片描述

    获取用户信息功能

    登录成功后我们要拿到当前用户的信息,存入userInfo对象,并在页面上简单展示

    <span>{{ userInfo }}span>
    

    创建一个获取当前用户的函数

    async getCurrentUser() {
        await request(
        `${this.host}${this.prefix}/User/GetCurrentUser`,
        "get",
        null
        )
        .catch((re) => {
            var res = re.response.data;
            this.errorMessage(res.error.message);
        })
        .then(async (re) => {
            var result = re.data.result as any;
            this.userInfo = result;
            this.token = getToken();
            clearInterval(this.timer);
    
            this.smsSendCd = 0;
            this.currentVerifyingType = null;
    
            this.successMessage("登录成功");
        });
    }
    

    最终效果

    在这里插入图片描述

    项目地址

    Github:matoapp-samples

  • 相关阅读:
    【Golang | gRPC】使用openssl生成证书
    MES在注塑制造领域的运用
    Linux下动态库和静态库制作与调用
    Pandas+Matplotlib 数据分析
    基于单片机的灭火机器人设计
    Python之三大基本库——Numpy(1)
    mysql同一个数据值,获取最新的一条数据
    win&linux下gradel安装
    js介绍及内置功能函数、数据类型、变量
    java-使用filereader读取文件乱码
  • 原文地址:https://www.cnblogs.com/jevonsflash/p/17309579.html