從零到部署:用 Vue 和 Express 實現迷你全棧電商應用(九):使用 Authing 打造擁有微信登陸企業級的用戶系統

若是您以爲咱們寫得還不錯,記得 點贊 + 關注 + 評論 三連,鼓勵咱們寫出更好的教程💪

若是你想在小程序裏面打造支持微信登陸的企業級用戶系統,能夠學習圖雀社區另一篇文章:Taro 小程序開發大型實戰(九):使用 Authing 打造具備微信登陸的企業級用戶系統javascript

在以前的迷你電商應用中,咱們的網站缺乏了一個關鍵組成部分:用戶鑑權系統,包括登陸、註冊、以及權限管理等相應的配置。徒手實現這些功能當然可行,可是對於一支崇尚精益的團隊來講,選擇可靠的身份認證服務(IDaaS)是更加明智的選擇,不只可以提供完善且豐富的身份認證和用戶鑑權功能,還確保遵循最佳安全實踐和優秀的可擴展性。在這篇教程中,咱們將手把手帶你在本系列以前完成的迷你電商應用中快速集成 Authing 用戶認證模塊,提供一致、流暢、安全的身份認證體驗。html

首先咱們先來看一下總體用戶系統接入後的效果:前端

添加用戶界面及路由

瞭解了效果以後,咱們爲你準備好了重構以後的迷你電商代碼,你能夠 Clone 這份代碼,而後跟着教程,補充集成用戶系統須要的:vue

git clone -b auth-start https://github.com/tuture-dev/vue-online-shop-frontend.git
# 或者下載 Gitee 上的倉庫
git clone -b auth-start https://gitee.com/tuture/vue-online-shop-frontend.git

代碼下載後,在 client/src/pages 目錄中建立 user 目錄,在接下來時間咱們會在其中實現全部與用戶系統相關的頁面。java

提示
本篇教程採用的是 Vue 2.x 版本,但這篇教程的核心是經過 Authing 集成用戶系咱們並無使用太多關於 Vue 的知識。

實現用戶系統相關頁面

首先,咱們在 user 頁面目錄下建立 Index.vue 用戶首頁組件,代碼以下:ios

<template>
  <div>
    <div class="container">
      <div class="container">
        <h1 class="user-title">
          <router-link to="/" tag="div">登陸/註冊</router-link>
        </h1>
        <router-view />
      </div>
    </div>
  </div>
</template>

<style>
.user-title:hover {
  cursor: pointer;
}

.container {
  margin-top: 40px;
}
</style>

接着建立 Login.vue 登陸組件,代碼以下:git

<template>
  <div id="login-form">用戶登陸</div>
</template>

<script>
export default {};
</script>

而後建立 Setting.vue 設置組件,代碼以下:github

<template>
  <div>settings</div>
</template>

<script>
export default {
  data() {
    return {
      model: { manufacturer: { name: "", _id: "" } }
    };
  },
  mounted() {}
};
</script>

配置中心路由

最後是在路由中集成上面定義的用戶系統相關頁面,修改 client/src/router/index.js 代碼以下:web

// ...
import UserIndex from "@/pages/user/Index";
import Login from "@/pages/user/Login";
import Setting from "@/pages/user/Setting";

Vue.use(Router);

const router = new Router({
  routes: [
    {
      path: "/",
      name: "Home",
      component: Home
    },
    {
      path: "/admin",
      name: "Admin",
      component: Index,
      children: [
        {
          path: "new",
          name: "New",
          component: New
        },
        {
          path: "",
          name: "Products",
          component: Products
        },
        {
          path: "edit/:id",
          name: "Edit",
          component: Edit
        },
        {
          path: "manufacturers",
          name: "Manufacturers",
          component: Manufacturers
        },
        {
          path: "manufacturers/new",
          name: "NewManufacturers",
          component: NewManufacturers
        },
        {
          path: "manufacturers/edit/:id",
          name: "EditManufacturers",
          component: EditManufacturers
        }
      ]
    },
    {
      path: "/cart",
      name: "Cart",
      component: Cart
    },
    {
      path: "/detail/:id",
      name: "Detail",
      component: Detail
    },
    {
      path: "/user",
      name: "User",
      component: UserIndex,
      children: [
        {
          path: "login",
          name: "Login",
          component: Login
        },
        {
          path: "settings",
          name: "Settings",
          component: Setting
        }
      ]
    }
  ]
});

export default router;

把項目跑起來,點擊右上角的登陸或註冊應該能夠成功地跳轉到登陸頁面(雖然如今還很簡陋哈):vuex

OK,接下來讓咱們具體地實現各個頁面吧~

使用 Authing 接入用戶系統

在這一步驟中,咱們將正式使用 Authing 接入用戶系統。Authing 是國內出色的身份認證雲,能讓咱們輕鬆集成身份認證相關的邏輯,對於我的開發者來講,其無償使用額度也是至關充足的。

首先,讓咱們訪問 Authing 官方網站,點擊右上角的登陸按鈕,以下圖所示:

image.png
進入到登陸頁面後,咱們輸入賬戶名和密碼,會直接爲咱們建立賬號:

進入到控制檯後,讓咱們建立一個新的用戶池(顧名思義,就是用來管理和存儲一系列用戶的數據和信息),以下圖所示:

在建立用戶池的時候,輸入咱們想要的用戶池名稱和專屬域名後,選擇類型爲 Web,最後點擊,咱們的第一個用戶池邊建立好了。點擊「基礎配置」書籤,能夠查看到剛纔建立用戶池的一些關鍵信息,特別是用戶池 ID,以下圖所示:

注意
後續應用開發時,全部的用戶池 ID( userPoolId)請替換成本身賬戶的真實 ID。

配置 Vuex Mutations 和 Store

因爲咱們的應用使用了 Vuex 來解決狀態管理問題,所以咱們首先須要定義身份驗證相關的 Mutation。這裏咱們定義兩個新的 Mutation:

  • SET_USER  :設置用戶身份數據
  • LOGOUT :退出登陸

client/src/store/mutation-types.js 中添加上面三個 Mutation 常量,代碼以下:

export const ALL_PRODUCTS = "ALL_PRODUCTS";
export const ALL_PRODUCTS_SUCCESS = "ALL_PRODUCTS_SUCCESS";

export const PRODUCT_BY_ID = "PRODUCT_BY_ID";
export const PRODUCT_BY_ID_SUCCESS = "PRODUCT_BY_ID_SUCCESS";

export const ADD_PRODUCT = "ADD_PRODUCT";
export const ADD_PRODUCT_SUCCESS = "ADD_PRODUCT_SUCCESS";

export const UPDATE_PRODUCT = "UPDATE_PRODUCT";
export const UPDATE_PRODUCT_SUCCESS = "UPDATE_PRODUCT_SUCCESS";

export const REMOVE_PRODUCT = "REMOVE_PRODUCT";
export const REMOVE_PRODUCT_SUCCESS = "REMOVE_PRODUCT_SUCCESS";

export const ADD_TO_CART = "ADD_TO_CART";
export const REMOVE_FROM_CART = "REMOVE_FROM_CART";

export const ALL_MANUFACTURERS = "ALL_MANUFACTURER";
export const ALL_MANUFACTURERS_SUCCESS = "ALL_MANUFACTURER_S";

export const MANUFACTURER_BY_ID = "MANUFACTURER_BY_ID";
export const MANUFACTURER_BY_ID_SUCCESS = "MANUFACTURER_BY_ID_SUCCESS";

export const ADD_MANUFACTURER = "ADD_MANUFACTURER";
export const ADD_MANUFACTURER_SUCCESS = "ADD_MANUFACTURER_SUCCESS";

export const UPDATE_MANUFACTURER = "UPDATE_MANUFACTURER";
export const UPDATE_MANUFACTURER_SUCCESS = "UPDATE_MANUFACTURER_SUCCESS";

export const REMOVE_MANUFACTURER = "REMOVE_MANUFACTURER";
export const REMOVE_MANUFACTURER_SUCCESS = "REMOVE_MANUFACTURER_SUCCESS";

export const SET_USER = "SET_USER";
export const UPDATE_USER = "UPDATE_USER";
export const LOGOUT = "LOGOUT";

而後咱們在 client/src/store/mutations.js 中實現上面定義用戶相關 Mutation,代碼以下:

// ...
  UPDATE_MANUFACTURER_SUCCESS,
  REMOVE_MANUFACTURER,
  REMOVE_MANUFACTURER_SUCCESS,
  SET_USER,
  UPDATE_USER,
  LOGOUT
} from "./mutation-types";
import { Message } from "element-ui";

export const userMutations = {
  [SET_USER](state, payload) {
    state.user = payload;
  },
  [LOGOUT](state) {
    state.user = {};
  }
};

export const productMutations = {
  [ALL_PRODUCTS](state) {
    // ...
    state.showLoader = false;

    const { productId } = payload;
    state.products = state.products.filter(
      product => product._id !== productId
    );
  },
  [UPDATE_PRODUCT](state) {
    state.showLoader = true;
  // ...

    const { product: newProduct } = payload;
    state.product = newProduct;
    state.products = state.products.map(product => {
      if (product._id === newProduct._id) {
        return newProduct;
      }
      // ...
    const { product } = payload;
    state.cart.push(product);
    Message({
      message: "恭喜你,成功加入購物車!",
      type: "success"
    });
  },
  [REMOVE_FROM_CART](state, payload) {
    const { productId } = payload;
    state.cart = state.cart.filter(product => product._id !== productId);
    Message({
      message: "恭喜你,成功移除購物車!",
      type: "success"
    });
  }
};

export const manufacturerMutations = {
  // ...
    state.showLoader = false;

    const { manufacturerId } = payload;
    state.manufacturers = state.manufacturers.filter(
      manufacturer => manufacturer._id !== manufacturerId
    );
  },
  [UPDATE_MANUFACTURER](state) {
    state.showLoader = true;
  // ...
    const { manufacturer } = payload;
    state.manufacturers = state.manufacturers.concat(manufacturer);
  }
};

最後咱們在 Vuex Store 中集成相應的狀態與 Mutation,修改 client/src/store/index.js ,代碼以下:

// ...
import { productGetters, manufacturerGetters } from "./getters";
import {
  productMutations,
  cartMutations,
  manufacturerMutations,
  userMutations
} from "./mutations";
import { productActions, manufacturerActions } from "./actions";

Vue.use(Vuex);

export default new Vuex.Store({
  strict: true,
  state: {
    // ...
    // userInfo
    user: {}
  },
  mutations: {
    ...productMutations,
    ...cartMutations,
    ...manufacturerMutations,
    ...userMutations
  },
  // ...
});

在根組件 App 中集成用戶邏輯

讓咱們打開根組件 client/src/App.vue,在其中添加一個 mounted 方法,使得在整個應用剛啓動時獲取並檢查用戶身份數據。修改代碼以下:

// ...

<script>
export default {
  name: "App",
  mounted() {
    const userInfo = localStorage.getItem("userInfo");

    if (userInfo) {
      this.$store.commit("SET_USER", JSON.parse(userInfo));
    }
  }
};
</script>

// ...

能夠看到,咱們從 localStorage 中檢查是否有 userInfo 數據,若是有的話經過 SET_USER Mutation 將用戶身份數據存入狀態中。

在頭部組件 Header 中集成用戶邏輯

打開頭部組件 client/src/components/Header.vue ,咱們在其中添加用戶系統相關的邏輯,修改代碼以下:

// ...

<script>
export default {
  props: ["activeIndex"],
  data() {
    return {
      model: { manufacturer: { name: "", _id: "" } }
    };
  },
  computed: {
    isLogged() {
      let token = this.$store.state.user.token;

      return !!token;
    },
    avatar() {
      let photo = this.$store.state.user.photo;

      return photo;
    }
  },
  methods: {
    handleLogout() {
      localStorage.removeItem("token");
      localStorage.removeItem("userInfo");
      this.$store.commit("LOGOUT");
    }
  }
};
</script>

能夠看到,咱們主要作了如下改變:

  • isLogged 從原先的 data 變成了一個計算屬性,經過從 Vuex Store 中獲取 token 是否存在來判斷是否登陸
  • 添加了 avatar 計算屬性,用於從 Store 中獲取用戶頭像
  • 實現了 handleLogout 方法,用於處理登出邏輯,包括從 localStorage 中去除 tokenuserInfo 數據,併發起一個 LOGOUT Mutation 用於更新 Store 的狀態

經過 Authing Guard 實現登陸頁面

Guard 是 Authing 推出的可嵌入登陸表單,可以讓咱們用幾行代碼爲整個應用集成登陸和註冊功能,集成後的效果以下:

總體效果仍是很 OK 的,並且咱們能夠經過一些配置項輕鬆實現定製,下面咱們就來看看怎麼實現吧。

首先,咱們經過引入 Authing UMD 構建文件來集成 Authing Guard。在 client/index.html 文件中經過 script 標籤引入:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width,initial-scale=1.0" />
    <title>vue-online-shop</title>
  </head>
  <body>
    <div id="app"></div>
    <!-- built files will be auto injected -->
    <script src="https://cdn.jsdelivr.net/npm/@authing/guard/dist/Guard.umd.min.js"></script>
  </body>
</html>

接着打開登陸頁面組件 client/src/pages/user/Login.vue,修改代碼以下:

<template>
  <div id="login-form"></div>
</template>

<script>
export default {
  data() {
    return {
      model: { manufacturer: { name: "", _id: "" } }
    };
  },
  mounted() {
    const appId = "";
    const userPoolId = "";
    const domain = "https://tuture-first.authing.co";

    const form = new Guard(userPoolId, {
      logo: "https://tuture.co/images/avatar.png",
      title: "圖雀全棧迷你電商",
      mountId: "login-form",
      hideClose: true
    });

    const that = this;

    form.on("authenticated", userInfo => {
      that.$store.commit("SET_USER", userInfo);
      localStorage.setItem("token", JSON.stringify(userInfo.token));
      localStorage.setItem("userInfo", JSON.stringify(userInfo));

      that.$router.push("/");
    });
  }
};
</script>

咱們在 mounted 生命週期方法中初始化 Guard 實例。在初始化 Guard 實例時,第一個參數是用戶池 ID(記得換成本身的用戶池 ID!),能夠經過 Authing 控制檯獲取,第二個參數則是 Guard 組件的一些選項參數:

  • logo 是咱們整個網站的 Logo 圖片連接
  • title 是整個登陸表單的標題
  • mountId 是用於掛載登陸表單的 DOM ID,這裏就是模板中惟一的 div 元素 login-form
  • hideClose 用於隱藏關閉按鈕,由於咱們把登陸作成了一個獨立的頁面,不但願用戶把登陸表單關掉(這樣整個頁面就一片空白啦)
提示
關於 Guard 完整的構造函數 API,請參考 官方文檔

在初始化 Guard 組件後,咱們還須要添加身份驗證成功後的監聽事件函數,即 form.on("authenticated", handler) 。能夠看到,在回調函數中,咱們作了三件事:

  1. 發出 SET_USER Mutation,修改 Store 狀態
  2. localStorage 中存儲登陸後獲取的用戶信息
  3. 經過 $router 路由重定向到首頁
提示
更多回調事件,可參考 完整事件列表

配置完成後,開啓應用,點擊登陸按鈕,就能夠看到咱們炫酷的登陸頁面了:

看上去很不錯!

添加權限管理和路由守衛

在這一步中,咱們將配置權限管理和路由守衛。權限管理很容易理解,就是當用戶進行某些須要登陸的操做(例如添加到購物車)時判斷是否已經登陸,若是未登陸則重定向到登陸頁面。所謂路由守衛(或稱導航守衛),就是在進入一個具體的路由(頁面)以前,判斷用戶是否具有足夠的權限,若是權限不夠,則直接重定向到登陸頁面,不然容許進入該頁面。

在咱們的應用中,主要有三個地方須要配置權限:

  • 商品添加按鈕(ProductionButton)
  • 購物車(Cart)
  • 後臺管理(Admin)

讓咱們逐個擊破吧。

添加商品添加按鈕的權限管理

首先,咱們須要爲商品添加按鈕配置權限管理。打開 client/src/components/products/ProductButton.vue 組件,修改 methods 中的 addToCartremoveFromCart 方法,代碼以下:

// ...
<script>
export default {
  // ...
  methods: {
    addToCart() {
      const token = localStorage.getItem("token");
      const that = this;

      if (token) {
        this.$store.commit("ADD_TO_CART", {
          product: this.product
        });
      } else {
        this.$confirm(
          "你還未登陸,點擊去登陸跳轉登陸頁面,點擊取消回到主界面",
          "提示",
          {
            confirmButtonText: "去登陸",
            cancelButtonText: "取消",
            type: "warning"
          }
        )
          .then(() => {
            that.$router.push("/user/login");
          })
          .catch(() => {
            this.$message({
              type: "info",
              message: "你已取消"
            });
          });
      }
    },
    removeFromCart(productId) {
      const token = localStorage.getItem("token");
      const that = this;

      if (token) {
        this.$store.commit("REMOVE_FROM_CART", {
          productId
        });
      } else {
        this.$alert(
          "點擊去登陸跳轉登陸頁面,點擊取消回到主界面",
          "你還未登陸",
          {
            confirmButtonText: "去登陸",
            cancelButtonText: "取消"
          }
        )
          .then(() => {
            that.$router.push("/user/login");
          })
          .catch(() => {
            this.$message({
              type: "info",
              message: "你已取消"
            });
          });
      }
    }
  }
};
</script>

能夠看到,實現權限管理的思路很簡單:先從 localStorage 中判斷用於鑑權的 token 是否存在,若是存在則代表已登陸,執行相應的 Mutation;若是不存在 token,則彈出 Alert 提示框詢問用戶是否須要跳轉到登陸頁面進行登陸。

實現購物車的路由守衛

而後咱們來實現購物車的路由守衛。幸運的是,Vue Router 已經爲咱們提供了組件級別的路由守衛的方法 beforeRouteEnter 。打開 client/src/pages/Cart.vue,修改代碼以下:

// ...
<script>
// ...

export default {
  name: "home",
  // ...
  beforeRouteEnter(to, from, next) {
    const token = localStorage.getItem("token");

    if (!token) {
      next("/user/login");
    } else {
      next();
    }
  }
};
</script>

依然是經過 localStorage 中嘗試獲取 token 來判斷登陸狀態,而後經過 next 函數進入合適的路由。

實現後臺管理的路由守衛

相似地,咱們實現後臺管理頁面的路由守衛。打開 client/src/pages/admin.Index.vue,添加路由守衛方法,代碼以下:

// ...
<script>
// ...

export default {
  // ...
  beforeRouteEnter(to, from, next) {
    const token = localStorage.getItem("token");

    if (!token) {
      next("/user/login");
    } else {
      next();
    }
  }
};
</script>

完成這一步後,打開應用,咱們來看一下添加了權限管理和路由守衛的以後的應用會是怎麼樣的:

將用戶系統與現有的數據庫集成

僅僅實現登陸和註冊功能是遠遠不夠的,咱們還須要將用戶系統集成到現有的數據庫中。例如咱們在添加商品時,但願可以和具體的用戶綁定。

所幸咱們使用的是 MongoDB 數據庫,所以不像傳統的關係型數據庫那樣須要繁雜的表結構更新,只需修改數據模型定義便可。

更新 Mongoose 數據定義

首先讓咱們來更新一波 Mongoose 數據定義。打開 server/model/index.js ,修改代碼以下:

// ...

const productSchema = Schema({
  id: ObjectId,
  name: String,
  image: String,
  price: String,
  description: String,
  user: String,
  manufacturer: { type: ObjectId, ref: "Manufacturer" },
});

const manufacturerSchema = Schema({
  id: ObjectId,
  name: String,
  user: String,
});

// ...

能夠看到,咱們主要是在兩個數據模型 productSchemamanufacturerSchema 中加入了 user 字段,其餘均無需改變。

配置 Vuex Action

接着咱們修改項目的 Action,主要是在兩個新增數據的 Action(addProductaddManufacturer)建立模型時記錄用戶數據。打開 client/src/store/actions.js,修改代碼以下:

// ...
export const productActions = {
  // ...
  addProduct({ commit, state }, payload) {
    commit(ADD_PRODUCT);

    const { product } = payload;
    const _id = state.user._id;
    axios
      .post(`${API_BASE}/products`, {
        ...product,
        user: _id,
        manufacturer: product.manufacturer._id
      })
      .then(response => {
        // ...
      })
      .catch(() => {
        // ...
      });
  }
};

export const manufacturerActions = {
  // ...
  addManufacturer({ commit, state }, payload) {
    commit(ADD_MANUFACTURER);
    const { manufacturer } = payload;
    const _id = state.user._id;

    axios
      .post(`${API_BASE}/manufacturers`, { ...manufacturer, user: _id })
      .then(response => {
        // ...
      })
      .catch(() => {
        // ...
      });
  }
};

這裏咱們在前端發起請求建立新數據時,把 user_id 也傳了進去,這樣數據庫裏面對應的商品和製造商就會記錄相應的用戶 ID 啦。

添加帳戶設置並修改信息

在最後一步中,咱們將藉助 Authing SDK 實現更細粒度的用戶身份管理,以及我的信息設置頁面。首先用 npm 安裝 Authing 的 JavaScript SDK:

npm install authing-js-sdk

修改 Header 的賬戶設置連接

首先,讓咱們修改 Header 頭部中的賬戶設置連接。打開 client/src/components/Header.vue,修改代碼以下:

<template>
  <div class="header">
    // ...
    <div class="header-right">
      <el-dropdown v-if="isLogged">
        <el-avatar class="el-dropdown-link" :src="avatar"></el-avatar>
        <el-dropdown-menu slot="dropdown">
          <el-dropdown-item>
            <router-link to="/user/settings" tag="div">帳戶設置</router-link>
          </el-dropdown-item>
          <el-dropdown-item>
            <div @click="handleLogout">退出登陸</div>
          </el-dropdown-item>
        </el-dropdown-menu>
      </el-dropdown>
      // ...
    </div>
  </div>
</template>
 // ...

<script>
import Authing from "authing-js-sdk";

export default {
  // ...
  methods: {
    async handleLogout() {
      const userPoolId = "";

      const token = JSON.parse(localStorage.getItem("token"));
      const userId = JSON.parse(localStorage.getItem("userInfo"))._id;
      const authing = new Authing({
        userPoolId
      });

      try {
        const res = await authing.checkLoginStatus(token);
        console.log("res", res);

        await authing.logout(userId);

        this.$message({
          message: "成功登出",
          type: "success"
        });
      } catch (err) {
        console.log("err", err);
      }

      localStorage.removeItem("token");
      localStorage.removeItem("userInfo");
      this.$store.commit("LOGOUT");
    }
  }
};
</script>

能夠看到,咱們主要作了兩點變更:

  1. 在模板中調整「帳戶設置」的連接,從原先 Authing 的連接替換成了本應用的 /user/settings 路由,這個咱們後面立刻會實現
  2. handleLogout 方法中,咱們在 localStorage 抹去用戶信息以前,經過 authing.checkLoginStatus 檢查登陸狀態,而後經過 authing.logout 執行登出操做

實現 Setting 賬戶設置頁面

打開以前已經建立好的設置頁面 client/src/pages/user/Setting.vue,實現用戶我的信息設置頁面,代碼以下:

<template>
  <div>
    <app-header></app-header>
    <div class="user-container">
      <div class="user-form">
        <el-upload
          class="avatar-uploader"
          action="https://imgkr.com/api/files/upload"
          :show-file-list="false"
          :on-success="handleAvatarSuccess"
        >
          <img v-if="imageUrl" :src="imageUrl" class="avatar" />
          <i v-else class="el-icon-plus avatar-uploader-icon"></i>
        </el-upload>

        <el-form
          :model="user"
          :rules="rules"
          ref="ruleForm"
          label-width="100px"
          class="demo-ruleForm"
        >
          <el-form-item label="暱稱" prop="nickName">
            <el-input v-model="user.nickname"></el-input>
          </el-form-item>
          <el-form-item>
            <el-button type="primary" @click="submitForm('ruleForm')"
              >更新</el-button
            >
          </el-form-item>
        </el-form>
      </div>
    </div>
  </div>
</template>

<style>
.avatar-uploader .el-upload {
  border: 1px dashed #d9d9d9;
  border-radius: 6px;
  cursor: pointer;
  position: relative;
  overflow: hidden;
}
.avatar-uploader .el-upload:hover {
  border-color: #409eff;
}
.avatar-uploader-icon {
  font-size: 28px;
  color: #8c939d;
  width: 178px;
  height: 178px;
  line-height: 178px;
  text-align: center;
}
.avatar {
  width: 178px;
  height: 178px;
  display: block;
}

.user-form {
  width: 500px;
}

.user-container {
  display: flex;
  flex-direction: row;
  justify-content: center;
}
</style>

<script>
import Header from "@/components/Header.vue";
import Authing from "authing-js-sdk";

export default {
  data() {
    return {
      user: {},
      imageUrl: "",
      rules: {
        nickname: [
          { required: true, message: "請輸入你的暱稱", trigger: "blur" },
          { min: 3, max: 25, message: "長度在 3 到 25 個字符", trigger: "blur" }
        ]
      }
    };
  },
  created: function() {
    const user = this.$store.state.user;
    const userInfo = localStorage.getItem("userInfo");

    if (user && Object.keys(user).length === 0 && userInfo) {
      this.user = JSON.parse(userInfo);
      this.imageUrl = this.user.photo;
    } else {
      this.user = { ...user };
      this.imageUrl = user.photo;
    }
  },
  components: {
    "app-header": Header
  },
  methods: {
    async handleAvatarSuccess(res, file) {
      if (res.code === 200) {
        this.imageUrl = res.data;
      } else {
        this.$message.error("圖片上傳失敗");
      }
    },
    async submitForm(formName) {
      const nickname = this.user.nickname;
      const photo = this.imageUrl;
      const userId = this.user._id;
      const user = this.user;
      const that = this;

      this.$refs[formName].validate(async valid => {
        if (valid) {
          const token = localStorage.getItem("token");
          const userPoolId = "";

          const authing = new Authing({
            userPoolId
          });

          const login = await authing.login({
            email: "",
            password: ""
          });

          console.log("nickName", nickname);
          try {
            await authing.update({
              _id: login._id,
              photo,
              nickname
            });

            const newUser = { ...user, nickname, photo };
            localStorage.setItem("userInfo", JSON.stringify(newUser));
            that.$store.commit("SET_USER", newUser);

            this.$message({
              message: "修改信息成功",
              type: "success"
            });
          } catch (err) {
            console.log("err", err);

            this.$message.error("修改信息失敗");
          }
        } else {
          console.log("error submit!!");
          return false;
        }
      });
    }
  }
};
</script>

咱們主要看一下 scripttemplate 部分。首先在 script 部分中,咱們的組件包括:

  • data 字段定義了模板中所須要的數據,包括 userimageUrl(頭像連接)以及 rules(表單校驗規則)
  • created 生命週期方法用於從 Vuex Store 以及 localStorage 中獲取用戶數據(localStorage 的優先級更高一些),而後初始化上面的 data 字段
  • components 用於指定 app-header 組件爲咱們剛纔修改好的 Header 組件
  • methods 中定義了 handleAvatarSuccesssubmitForm 兩個 Handler,分別用於處理頭像上傳成功以及提交表單的邏輯。在 submitForm 方法中,咱們先從表單中獲取到相應的數據,而後經過 authing.update 更新用戶數據,成功後再修改 Vuex Store 中的狀態

調整 App 根組件

讓咱們調整一下 App 根組件。打開 client/src/App.vue ,修改代碼以下:

// ...

<script>
import Authing from "authing-js-sdk";

export default {
  name: "App",
  mounted() {
    this.checkLogin();
  },
  methods: {
    async checkLogin() {
      const token = localStorage.getItem("token");

      if (token) {
        const userPoolId = "";

        const authing = new Authing({
          userPoolId
        });

        const result = await authing.checkLoginStatus(JSON.parse(token));

        if (result.status) {
          const userInfo = localStorage.getItem("userInfo");

          if (userInfo) {
            this.$store.commit("SET_USER", JSON.parse(userInfo));
          }
        } else {
          localStorage.removeItem("token");
          localStorage.removeItem("userInfo");
        }
      }
    }
  }
};
</script>

// ...

能夠看到,咱們主要實現了一個 checkLogin 方法,用於在整個應用剛掛載時檢查登陸狀態,若是登陸成功,則從 storage 裏面取出數據並設置進 Redux Store ,若是登陸失效,則清空本地的 storage 信息。

調整其餘頁面

最後咱們調整一下其餘頁面的一些細節。修改 client/src/pages/user/Index.vue,代碼以下:

<template>
  <div>
    <div class="container">
      <router-view />
    </div>
  </div>
</template>

<style></style>

繼續修改 client/src/pages/user/Login.vue,代碼以下:

<template>
  <div>
    <h1 class="user-title">
      <router-link to="/" tag="div">用戶界面</router-link>
    </h1>
    <div id="login-form"></div>
  </div>
</template>

<style>
.user-title:hover {
  cursor: pointer;
}
</style>

// ...

當保存上面的修改的代碼,咱們能夠看到以下的效果:

集成微信、QQ 登陸或 Github 登陸

經過上述流程,咱們就完成了一個完整的用戶系統及其與現有系統的整合,可是有同窗發現了,咱們在平時生活或工做中,除了常規的手機號+驗證碼、郵箱密碼等,還會有一些更方便的登陸方式,如微信登陸、QQ登陸等,那麼咱們如何集成這些方便的登陸呢?實際上可能看起來很複雜,可是在咱們現有的基礎上,用 Authing 能夠很方便的集成微信、QQ登陸等。

注意
只有企業才能集成微信或 QQ 登陸,若是你是我的開發者,那麼這一節你能夠跳過哦🤓

集成微信掃碼登陸

首先去微信官方文檔完成註冊,而後申請一個微信網頁應用,而後獲取到微信網頁應用的 AppID 和 AppSecret:

接着滑動到底部,將受權回調域改成 oauth.authing.cn

而後咱們開始去 Authing 控制檯,在相應微信登陸裏面,填入剛剛獲取的 AppIDAppSecret

注意到上面咱們第三個參數 「重定向地址」 填寫了咱們如今 Vue 全棧電商應用的開發服務器地址,讀者應該根據本身當前的須要地址進行對應的填寫。

大功告成,經過上面的步驟咱們就配置好了微信網頁登陸,如今你應該能夠看到以下的效果:

個人天!好神奇!就是上面幾下手工點按配置,咱們就集成好了微信登陸!😅

集成 QQ 登陸

按照和接入微信網頁登陸相似的方式,咱們前往 QQ 互聯中心,註冊一個帳號,並建立一個網頁應用。

而後進入網頁應用,將受權回調地址填寫爲:[https://oauth.authing.cn/oauth/qq/redirect](https://oauth.authing.cn/oauth/qq/redirect) ,接着回到 Authing 控制檯,咱們配置 QQ 登陸:

保存以後,大功告成!咱們的應用裏面就有了 QQ 登陸,和微信登陸一樣簡單!

集成 Github 登陸

最後咱們再來嘗試集成一下開發者比較喜好的 Github 登陸,看看 Authing 是如何簡化這一勞動的呢?

首先根據 Github 指引,建立一個 OAuth 應用

而後填入以下內容:

其中紅框框出來的內容須要填入 Authing 相關的 [https://oauth.authing.cn/oauth/github/redirect](https://oauth.authing.cn/oauth/github/redirect),而後建立好應用以後,取出 Client IDClient Secret

以後就是相似以前的操做,進入 Authing 控制檯,配置 Github 相關的內容:

最後能夠看到以下效果:

小結

至此,本篇教程也就結束了,相信你已經感覺到了 Authing 身份認證機制的強大與便捷。在當今 Serverless 時代,愈來愈多標準化的流程(例如身份驗證、人工智能應用等等)正在逐漸邁向雲端,成爲一種可直接消費的資源,而咱們做爲應用終端的開發者則能夠將更多的時間和精力放在打磨和完善自身的產品上,在必定程度上解放了生產力。

圖雀社區秉承「加速技術傳播」的理念,致力於推廣可以真正讓開發者和用戶的生活變得更美好的技術。

想要學習更多精彩的實戰技術教程?來 圖雀社區逛逛吧。

相關文章
相關標籤/搜索