Vue3 プロジェクトで WeChat 認証ログインをエレガントに実装する方法

Vue3 プロジェクトで WeChat 認証ログインをエレガントに実装する方法

序文

WeChat 認証ログインは、WeChat パブリック アカウントを開発する際に避けられないトピックであり、認証ログイン プロセス全体の実装には、フロントエンドとバックエンドの連携が必要です。かつて、フロントエンドとバックエンドが分離されていなかった頃は、フロントエンドの私たちは、認可の具体的な実装についてあまり心配する必要がなかったのかもしれません。しかし、2021年現在、フロントエンドとバックエンドを分離するアーキテクチャが普及しており、フロントエンドとバックエンドを分離した状態でWeChat認証ログインをどのように実装するかが、今日議論されるべき重要な問題となっています。

準備する

まず、WeChat の認証プロセス全体を整理する必要があります。ここでは、公式ドキュメントを直接移動します。

ユーザーが WeChat クライアントでサードパーティの Web ページにアクセスすると、公式アカウントは WeChat Web ページの認証メカニズムを通じてユーザーの基本情報を取得し、ビジネス ロジックを実装できます。

...

ウェブページ認証の2つのスコープの違い

1. snsapi_base をスコープとして開始された Web ページ認証は、ページに入るユーザーの openid を取得するために使用されます。これはサイレント認証であり、自動的にコールバック ページにジャンプします。ユーザーは、コールバックページ(通常はビジネスページ)に直接アクセスしたと認識します。

2. スコープとして snsapi_userinfo を使用して開始された Web ページの認証は、基本的なユーザー情報を取得するために使用されます。ただし、このタイプの認証にはユーザーの手動による同意が必要であり、ユーザーが同意しているため、注意する必要はなく、認証後にユーザーの基本情報を取得できます。

...

具体的には、Web 認証プロセスは次の 4 つのステップに分かれています。

1. ユーザーを認証ページに移動させて認証に同意し、コードを取得するよう誘導します。

2. Web認証用のaccess_tokenのコードに交換する(基本サポートのaccess_tokenとは異なる)

3. 必要に応じて、開発者は有効期限切れを避けるためにWeb認証access_tokenを更新することができます。

4. Webページ認証access_tokenとopenidを通じて基本的なユーザー情報を取得する(UnionIDメカニズムをサポート)

ここに添付されているのは、WeChat パブリック アカウント開発のための WeChat 認可に関する公式文書です。

上記は私が抽出した重要な情報の一部です。もちろん、他にも説明はあります。初心者の読者は、まず公式ドキュメントを注意深く読んでいただければと思います。

ここで付け加えておきますが、上記のプロセスの 4 つのステップのうち、最初のステップを除き、残りの 3 つのステップはサーバー側で完了する必要があります。フロントエンドの核となるのは、実はユーザーのログイン状態をどのように確認・判断し、ログイン状態を維持するかという点です。

実装のアイデア

ご存知のとおり、Vue はフロントエンドとバックエンドを分離するテクノロジ ソリューションの製品です。純粋なフロントエンド アプリケーションです (サーバー側レンダリングを除く)。通常、ユーザーがページを開いてページの js スクリプトを実行するときは、サーバーデータを非同期的に要求し、関連するロジックを処理および判断する必要があります。 WeChat 認証ログインを実装するための前提は、まずユーザーがログインする必要があるかどうか (Cookie またはトークン) を判断する必要があることです。ユーザーがログインしていない場合は、認証ログインプロセスを実行する必要があります。認証ログインが成功した場合は、再度認証ログインをトリガーせずにページ切り替えを容易にするために、フロントエンドでログインステータスを記録する必要もあります。さらに分析を進めると、フロントエンドが実際に実行できることは、WeChat サーバーから提供されたコードを取得し、そのコードをバックエンドに渡すことで、バックエンドが後続の手順を完了し、ユーザー情報を取得してユーザーを生成することであることがわかります。次に、プロセス全体を次のように要約します。

  1. (フロントエンド) ユーザーがログインしているかどうかを確認します。
  2. (フロントエンド)ログインしていない場合は、ユーザーを認証ページに誘導し、認証に同意してコードを取得します。
  3. (フロントエンド)取得したコードをバックエンドに送信する
  4. (バックエンド)コードをユーザー認証情報openidと交換する
  5. (バックエンド) openid を通じてユーザーが存在するかどうか、新しいユーザーを登録する必要があるかどうかを確認し、ユーザー ID を取得します。
  6. (バックエンド) ユーザー情報を返します。
  7. (フロントエンド) ユーザーのログイン状態を記録し、ログイン前のページに戻ります。

このプロセスを次のように図に描きました。

コードについて

上記のアイデアに基づいて、コーディングフェーズを開始します。作者はVue3を使用しており、Vue2開発者は状況に応じて適切な調整を行ってください。
ユーザー認証ログインロジックを容易にするために、認証ログインをログインページとしてシールするつもりです。これの利点は、ログインが必要と判断された場所であればどこでも、Vue Router の push メソッドを介してログインページに直接ジャンプできることです。

通常、アプリケーションのすべてのページにアクセスするためにログインが必要なわけではありません。特定のページにアクセスする場合にのみ、ユーザーはログインする必要があります。次に、どのページにログイン認証が必要かを特定する必要があります。ここでは、識別のために Vue Router の meta 属性を使用できます。公式ドキュメントでは、meta について次のように説明されています。

場合によっては、遷移名やルートにアクセスできるユーザーなど、任意の情報をルートに添付したいことがあります。これらのことは、ルート アドレスとナビゲーション ガードの両方でアクセスできるプロパティ オブジェクトのメタ プロパティを受け取ることによって実行できます。

偶然にも、Vue Router には次のような公式の例があります。

定数ルート = [
  {
    パス: '/posts',
    コンポーネント: PostsLayout、
    子供たち: [
      {
        パス: 'new'、
        コンポーネント: PostsNew、
        // アクセスするためにログインが必要なページ meta: { requiresAuth: true }
      },
      {
        パス: ':id',
        コンポーネント: PostsDetail、
        // 誰でもアクセスできるページ meta: { requiresAuth: false }
      }
    ]
  }
]

次に、Vue RouterのグローバルガードbeforeEachでこのメタ情報を取得してログインジャンプを実行します。

router.beforeEach((to, from) => {
  // 各ルートレコードをチェックする代わりに // to.matched.some(record => record.meta.requiresAuth)
  to.meta.requiresAuth の場合、userStore.isLogin の場合、
    // このルートは認証が必要です。ログインしているかどうかを確認してください。 // そうでなければ、ログインページにリダイレクトします。 return {
      パス: '/login',
      // 後で再度クエリを実行できるように場所を保存します: { redirect: to.fullPath },
    }
  }
})

説明する必要があるのは、userStore.isLogin の実装です。これは、実際に採用しているログイン状態維持ソリューションに関係しています。トークン方式を採用している場合は、トークンがすでに存在するかどうかを確認します。作者は vuex を使用してトークンを保存し、プラグインを使用して Store 内のデータを localStorage に永続化します。

次に、具体的な実装を見てみましょう。

login.vue: ログインコンポーネント

<テンプレート>
  <div class="ログイン"></div>
</テンプレート>

<script lang="ts">
'vue' から {defineComponent} をインポートします。

'@/hooks/useWechatAuth' から { jump2Auth, getUserInfo } をインポートします。
'@/store/modules/user' から { userStore } をインポートします。
'@/hooks/usePage' から { redirectTo, getRouteQuery } をインポートします。

エクスポートデフォルトdefineComponent({
  名前: 'ログイン',
  設定() {
    code = getRouteQuery().code を文字列として
    // 3. コードがある場合は承認されている if (code) {
      getUserInfo(コードを文字列として取得)。次に((res: any) => {
        //レコードトークン
        userStore.saveToken(res.access_token)
        const リダイレクト = userStore.userState.landPageRoute || '/'
        // 認証前に訪問したページにジャンプする redirectTo(redirect)
      })
    } それ以外 {
      // 1. 前のページのアドレスを記録する const { redirect } = getRouteQuery()
      if (リダイレクト) {
        userStore.setLandPage(文字列としてリダイレクト)
      }
      // 2. ジャンプ認証 const callbackUrl = window.location.origin + window.location.pathname
      jump2Auth(コールバックURL)
    }
  },
})
</スクリプト>

ご覧のとおり、ログインページには実際にはコンテンツがありません。このページにジャンプした後、WeChat認証ページに直接リダイレクトします。認証コールバックもこのページに戻ります。このとき、ルーティングパラメータを取得してコードパラメータを取得します。

@/hooks/usePage.ts: このファイルは主にルーターに関連する一般的なメソッドをカプセル化します

'@/router' からルーターをインポートします
'lodash' から { cloneDeep } をインポートします
'vue' から { toRaw } をインポートします

/**
 * リダイレクト * @param path パス */
エクスポート関数redirectTo(path: string) {
  const { replace } = ルーター
  交換する({
    パス、
  })
}

/**
 * ルート上のクエリパラメータを取得します */
エクスポート関数 getRouteQuery() {
  const { currentRoute } = ルーター
  const { クエリ } = currentRoute.value
  cloneDeep(クエリ)を返す
}

@/hooks/useWechatAuth.ts: このファイルは、バックエンドとのやり取りのためのWeChat認証リクエストをカプセル化します。

'@/hooks/useAxios' から { useAxios } をインポートします。

/**
 * WeChatによって承認されたリダイレクトアドレスを取得します* @param callbackUrl 承認後のコールバックリンク* @returns
 */
エクスポート関数 jump2Auth(callbackUrl: 文字列) {
  使用Axios({
    URL: '/api/wechat/auth',
    パラメータ: {
      リダイレクトURL: コールバックURL、
    },
  }).then((authUrl: 任意) => {
    process.env.NODE_ENV === '開発'の場合{
      window.location.href = コールバック URL + '?code=test'
    } それ以外 {
      window.location.href = 認証URL
    }
  })
}

/**
 * ログインするにはコードを送信してください * @param code
 * @戻り値
 */
非同期関数 getUserInfo(コード: 文字列) をエクスポートします。
  const userInfo = useAxios({
    メソッド: 'POST'、
    URL: '/api/wechat/auth',
    パラメータ: {
      コード、
    },
  })
  ユーザー情報を返す
}

@/store/modules/user.ts: グローバル状態ストレージ。主に、ログイン前にアクセスしたトークンとページを記録します。

'vuex-module-decorators' から { Module、VuexModule、Mutation、getModule、Action } をインポートします。
'@/store' からストアをインポートします
'../globals' から { initialUnencryptedStorage } をインポートします。

インターフェースUserState{
  トークン: 文字列
  landPageRoute: 文字列
}

定数NAME = 'ユーザー'
// name: モジュール名 // namespaced は名前空間を開くことを意味します // dynamic は true に設定されており、動的モジュールを作成し、実行時にモジュールをストレージに登録することを意味します // preserveState データが永続化されている場合、この変数が true の場合、初期値はストレージから取得できます @Module({
  名前空間: true、
  名前: NAME、
  動的: true、
  店、
  preserveState: ブール値(initialUnencryptedStorage[NAME])、
})
エクスポートクラスUserはVuexModuleを拡張します。
  ユーザー状態: ユーザー状態 = {
    トークン: ''、
    /** ログインする前にページにアクセスする */
    土地ページルート: '',
  }

  isLogin() を取得: ブール値 {
    !!this.userState.token を返す
  }

 
  @突然変異
  saveToken(トークン: 文字列): void {
    this.userState.token = トークン
  }

  @突然変異
  setLandPage(ルート: 文字列): void {
    this.userState.landPageRoute = ルート
  }
}

エクスポートconst userStore = getModule<User>(User)

作者は、vuex-persistedstate プラグインを使用して、ストア内のデータを localStorage に保存します。これの利点は、ユーザーがページを閉じた後、WeChat 認証プロセスを再トリガーせずに再度ページにアクセスできるため、ユーザー エクスペリエンスが大幅に最適化されることです。

要約する

コードの抽象化と再利用の点では、Vue3 の方がはるかに書きやすいと言わざるを得ません。皆さんが公式のプラクティスに従って、ロジック コードを分離し、フックを 1 つずつ生成して、コードがよりエレガントに見えるようにしていただければと思います。著者は、このソリューションがコードの簡潔さとエレガントさ、そしてビジネス ニーズの実現という点でほぼ完璧であることを試して実証しました (自慢させてください)。もちろん、私が発見していないバグや問題点があるかもしれません。結局のところ、完璧なアーキテクチャというものは存在しません。また、読者の皆さんが私と議論し、より良い解決策やアイデアを提供してくれることも歓迎します。

Vue3 プロジェクトで WeChat 認証ログインをエレガントに実装する方法についての記事はこれで終わりです。Vue3 WeChat 認証ログインに関する関連コンテンツについては、123WORDPRESS.COM の過去の記事を検索するか、以下の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

以下もご興味があるかもしれません:
  • Vue3 でモバイル ログインおよび登録モジュールをエレガントに実装する方法
  • Vue3 カプセル化ログイン機能の 2 つの実装

<<:  MySQLがウィンドウ関数で合計関数を実行するときに発生する可能性のあるバグ

>>:  Nginxにモジュールを動的に追加する方法

推薦する

はじめに: HTML の基本的なタグと属性の簡単な紹介

HTML はタグと属性で構成されており、これらを組み合わせてブラウザにページの表示方法を指示します。...

JSは10進数を16進数に変換するサンプルコードを実装します

序文コードを書くと、時々基数変換の問題に遭遇します。一般的な変換には、2進数、8進数、10進数、16...

CSS3 で半透明の背景画像と不透明なコンテンツを実現する方法の例

以前のブログのログインページを作成していたときに、この問題に遭遇しました。突然、透明な背景画像と不透...

ページのキャッシュを防ぐソリューション

解決: <head> に次のコードを追加します。コードをコピーコードは次のとおりです。 ...

nginx を使用してカナリアリリースをシミュレートする方法

この記事では、ブルーグリーン デプロイメントと、nginx を使用してカナリア リリースを最も簡単な...

Mac OS10.12 に mysql5.7.18 をインストールするチュートリアル

ウェブ全体を検索して、さまざまな落とし穴を見つけましたが、問題は解決しませんでした。ついに自分でも分...

js は axios 制限リクエスト キューを実装します

目次背景は次のとおりです。何が起こるでしょうか?背景は次のとおりです。実際の開発では、ネットワークの...

テーブルパーティションとパーティション分割とは何ですか?MySqlデータベースパーティションとテーブルパーティション分割方法

1. テーブルとパーティションを分割する必要があるのはなぜですか?日常の開発では、大きなテーブルに遭...

IE6 スペースバグ修正方法

コードを見てみましょう:コードをコピーコードは次のとおりです。 < !DOCTYPE html...

Reactのようなフレームワークをゼロから作成する

最近、インターネットで「Build your own React」という記事を見ました。著者は、シン...

HTML テキストフォーマットの簡単な例 (詳細な説明)

1. テキストの書式設定: この例では、HTML ファイル内のテキストを書式設定する方法を示します...

元のPATHを上書きしてコマンドが見つからないというメッセージが表示されるコマンド失敗の問題を解決する方法

同僚から、LINUX サーバー上の多くのコマンドが (コマンドが見つかりません) というプロンプトで...

入力ファイルのカスタムボタンの美化(デモ)

以前にも同じような記事を書いたことがありますが、js スクリプトを使用しており、ファイルパスを表示で...

CSS3は水平方向の中央揃え、垂直方向の中央揃え、水平方向と垂直方向の中央揃えのサンプルコードを実装しています。

フロントエンドの担当者であれば、面接でも仕事中でも、「CSS を使用して中央揃えにする」という効果に...

一般的なテーブルコンポーネントの Vue カプセル化の完全な手順記録

目次序文テーブル コンポーネントをカプセル化する必要があるのはなぜですか?ステップ1: 共通コンポー...