開発者ドキュメント

Nuxt.jsアプリで認証を実装する方法

序章

このチュートリアルでは、Nuxt.jsアプリで認証を実装します。 Auth モジュール。

このチュートリアルでは、以下を使用します。 JWT 認証用。

以下は、このチュートリアルで構築するものの簡単なデモです。

このアプリケーションのソースコードはGitHubにあります。

警告:このチュートリアルのいくつかのパッケージには、既知の脆弱性を持つ依存関係が含まれています。 本番環境では、これらのパッケージをアップグレードするか、代替手段を見つけるか、パッチが適用された修正を含むフォークバージョンを作成することで、これらの問題を解決します。 ただし、チュートリアルの限られたコンテキスト内では、そのままの教育的価値を提供します。

前提条件

このチュートリアルを完了するには、次のものが必要です。

Vue.jsとNuxt.jsにある程度精通していると有益な場合があります。 Nuxt.jsを使い始めた場合は、この投稿を参照できます。

このチュートリアルは、Node v13.13.0、npm v6.14.4、 vue v2.6.11、および nuxt v2.12.2。

ステップ1—サンプルAPIを起動する

自分に最適なフレームワークを自由に使用できます。 ただし、迅速な開発のために、このチュートリアルではAdonisJsで構築されたAPIのクローンを作成します。

APIは以下を利用します:

APIには3つのエンドポイントがあります。

まず、ターミナルウィンドウで次のコマンドを実行します。

  1. git clone https://github.com/do-community/jwt-auth-api.git

次に、プロジェクトディレクトリに移動します。

  1. cd jwt-auth-api

そして、APIの依存関係をインストールします。

  1. npm install

:インストールを実行すると、次の問題が発生する可能性があります。 sqlite3 バージョン 4.0.1 実行しているノードのバージョンによって異なります。 ご使用の環境との互換性を判断するには、changelogを参照してください。

当初の発行時点では、Nodeの最新バージョンは10でした。 1つのオプションは、Nodeのバージョンをにダウングレードすることです 10.20.1 (サポートが終了に近づいていることを理解した上で)。 次に、実行します npm install.

2番目のオプションは、 package-lock.json システムに検索させるファイル 4.2.0 これはノード13までサポートされています。 Nodeのバージョンをにダウングレードする必要がある場合もあります 13.13.0. 次に、実行します npm install.

3番目のオプションは変更することです package.json のバージョンに sqlite3 Nodeの現在のバージョンでサポートされている場合は、削除してください package-lock.json、および実行 npm install. ただし、テスト時には、 5.0.0 Node14以降のサポートを処理するためにまだリリースされていません。

非互換性の他の症状には、次のエラーが含まれます。 TypeError: Cannot read property 'data' of undefinedError: Cannot find module '[...]/node_modules/sqlite3/lib/binding/[...]/node_sqlite3.node'.

次に、名前を変更します .env.example.env:

  1. mv .env.example .env

そして、を生成します APP_KEY:

  1. npx @adonisjs/cli@4.0.12 key:generate

君は見るべきだ:

  1. Output
    generated: unique APP_KEY

それが完了したら、移行を実行してみましょう。

  1. npx @adonisjs/cli@4.0.12 migration:run

これで、APIを開始できます。

  1. # ensure that you are in the `jwt-auth-api` project directory
  2. npm start

APIにアクセスできます http://127.0.0.1:3333/api. チュートリアルの残りの期間は、これをターミナルウィンドウで実行したままにします。

ステップ2—Nuxt.jsアプリを作成する

これで、Nuxt.jsアプリを作成できます。 新しいターミナルウィンドウを開いて使用する vue-cli Nuxtスターターテンプレートを使用して新しいVueプロジェクトを初期化するには:

  1. npx vue-cli@2.9.6 init nuxt/starter nuxt-auth

注:テスト時、 vue-cli 非推奨です。 @vue/cli Vueプロジェクトの現在のコマンドラインツールです。 と @vue/cli-init レガシーに推奨されるアプローチです vue-cli プロジェクト。 でも、 create-nuxt-app 現代のNuxtプロジェクトに推奨されるアプローチです。

次に、プロジェクトディレクトリに移動する必要があります。

  1. cd nuxt-auth

そして、依存関係をインストールします。

npm install

次に、アプリを起動できます。

  1. npm run dev

アプリはで実行されている必要があります http://localhost:3000. Webブラウザーでアプリケーションを表示して、によって作成されたデフォルトのVueアプリケーションを確認できます。 vue-cli.

ステップ3—必要なNuxt.jsモジュールをインストールする

それでは、アプリに必要なNuxt.jsモジュールをインストールしましょう。 NuxtAuthモジュールNuxtAxiosモジュールを使用することになります。 auth モジュールは内部でAxiosを利用します:

  1. # ensure that you are in the `nuxt-auth` project directory
  2. npm install @nuxtjs/auth@4.5.1 @nuxtjs/axios@5.3.1 --save

それが完了したら、開きます nuxt.config.js:

  1. nano nuxt.config.js

以下のコードをに追加します nuxt.config.js:

nuxt.config.js
module.exports = {
  // ...

  modules: [
    '@nuxtjs/axios',
    '@nuxtjs/auth'
  ],
}

注:この時点で、Nuxtの新しいバージョンでエラーが発生する可能性があります。 Enable vuex store by creating 'store/index.js'. このエラーは、ストアディレクトリに空のindex.jsファイルを追加することで解決できます。

次に、モジュールを設定する必要があります。 以下のコードをに貼り付けます nuxt.config.js:

nuxt.config.js
module.exports = {
  // ...

  axios: {
    baseURL: 'http://127.0.0.1:3333/api'
  },

  auth: {
    strategies: {
      local: {
        endpoints: {
          login: { url: 'login', method: 'post', propertyName: 'data.token' },
          user: { url: 'me', method: 'get', propertyName: 'data' },
          logout: false
        }
      }
    }
  }
}

ここでは、Axiosがリクエストを行うときに使用するベースURLを設定します。 この例では、前に設定したサンプルAPIを参照しています。

次に、認証エンドポイントを定義します local APIの戦略に対応する戦略:

ステップ4—ナビゲーションバーコンポーネントを作成する

アプリのスタイルを設定するには、ブルマを利用できます。

開ける nuxt.config.js 以下のコードを link 内部にあるオブジェクト head 物体:

nuxt.config.js
module.exports = {
  // ...
  head: {
    // ...
    link [
      // ...
      {
        rel: 'stylesheet',
        href: 'https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.1/css/bulma.min.css'
      }
    ]
  },
  // ...
}

それでは、Navbarコンポーネントを作成しましょう。

  1. nano components/Navbar.vue

そして、次のコードを追加します。

コンポーネント/Navbar.vue
<template>
  <nav class="navbar is-light">
    <div class="container">
      <div class="navbar-brand">
        <nuxt-link class="navbar-item" to="/">Nuxt Auth</nuxt-link>
        <button class="button navbar-burger">
          <span></span>
          <span></span>
          <span></span>
        </button>
      </div>
      <div class="navbar-menu">
        <div class="navbar-end">
          <div class="navbar-item has-dropdown is-hoverable">
            <a class="navbar-link">
              My Account
            </a>
            <div class="navbar-dropdown">
              <nuxt-link class="navbar-item" to="/profile">My Profile</nuxt-link>
              <hr class="navbar-divider"/>
              <a class="navbar-item">Logout</a>
            </div>
          </div>
          <template>
            <nuxt-link class="navbar-item" to="/register">Register</nuxt-link>
            <nuxt-link class="navbar-item" to="/login">Log In</nuxt-link>
          </template>
        </div>
      </div>
    </div>
  </nav>
</template>

Navbarコンポーネントには、へのリンクが含まれています login, register, profile、 と logout.

次に、デフォルトのレイアウトを更新して、 Navbar 成分。

開ける default.vue:

  1. nano layouts/default.vue

そして、コンテンツを次のように置き換えます。

layouts / default.vue
<template>
  <div>
    <Navbar/>
    <nuxt/>
  </div>
</template>

<script>
import Navbar from '~/components/Navbar'

export default {
  components: {
    Navbar
  }
}
</script>

また、ホームページを更新しましょう。

開ける index.vue:

  1. nano pages/index.vue

そして、コンテンツを次のように置き換えます。

pages / index.vue
<template>
  <section class="section">
    <div class="container">
      <h1 class="title">Nuxt Auth</h1>
    </div>
  </section>
</template>

この時点で、次のタイトルを表示するアプリケーションが必要です。 "Nuxt Auth" ナビゲーションリンク付きのヘッダーバー付き:

ステップ5—ユーザー登録の処理

内部 pages ディレクトリ、新しいを作成します register.vue ファイル:

  1. nano pages/register.vue

そして、次のコードを追加します。

pages / register.vue
<template>
  <section class="section">
    <div class="container">
      <div class="columns">
        <div class="column is-4 is-offset-4">
          <h2 class="title has-text-centered">Register!</h2>

          <Notification :message="error" v-if="error"/>

          <form method="post" @submit.prevent="register">
            <div class="field">
              <label class="label">Username</label>
              <div class="control">
                <input
                  type="text"
                  class="input"
                  name="username"
                  v-model="username"
                  required 
                />
              </div>
            </div>
            <div class="field">
              <label class="label">Email</label>
              <div class="control">
                <input
                  type="email"
                  class="input"
                  name="email"
                  v-model="email"
                  required
                />
              </div>
            </div>
            <div class="field">
              <label class="label">Password</label>
              <div class="control">
                <input
                  type="password"
                  class="input"
                  name="password"
                  v-model="password"
                  required
                />
              </div>
            </div>
            <div class="control">
              <button type="submit" class="button is-dark is-fullwidth">Register</button>
            </div>
          </form>

          <div class="has-text-centered" style="margin-top: 20px">
            Already got an account? <nuxt-link to="/login">Login</nuxt-link>
          </div>
        </div>
      </div>
    </div>
  </section>
</template>

<script>
import Notification from '~/components/Notification'

export default {
  components: {
    Notification,
  },

  data() {
    return {
      username: '',
      email: '',
      password: '',
      error: null
    }
  },

  methods: {
    async register() {
      try {
        await this.$axios.post('register', {
          username: this.username,
          email: this.email,
          password: this.password
        })

        await this.$auth.loginWith('local', {
          data: {
          email: this.email,
          password: this.password
          },
        })

        this.$router.push('/')
      } catch (e) {
        this.error = e.response.data.message
      }
    }
  }
}
</script>

これには、次の3つのフィールドを持つフォームが含まれています。 username, email、 と password. 各フィールドは、コンポーネントの対応するデータにバインドされます。 フォームが送信されると、 register メソッドが呼び出されます。 Axiosモジュールを使用して、 /register エンドポイント、ユーザーデータを渡します。 登録が成功した場合は、Authモジュールを利用します loginWith()、を使用して local 戦略とユーザーデータを渡してユーザーをログインさせます。 次に、ユーザーをホームページにリダイレクトします。 登録中にエラーが発生した場合は、 error API応答から取得したエラーメッセージとしてのデータ。

エラーが発生した場合、エラーメッセージは通知コンポーネントによって表示されます。

新しいを作成します Notification.vue 内部のファイル components:

  1. nano components/Notifaction.vue

そして、その中に以下のコードを貼り付けます。

components / Notification.vue
<template>
  <div class="notification is-danger">
    {{ message }}
  </div>
</template>

<script>
export default {
  name: 'Notification',
  props: ['message']
}
</script>

通知コンポーネントは、 message エラーメッセージである小道具。

これで、ユーザー登録をテストできます。

ステップ6—ログインおよびログアウトしたLoggedUsersの処理

登録が成功すると、ユーザーはログインする必要がありますが、現在、ユーザーがログインしているかどうかをアプリが知る方法はありません。 それでは、Navbarコンポーネントを更新し、計算されたプロパティをいくつか追加して、これを修正しましょう。

その前に、まずVuexストアを作成してアクティブ化しましょう。 index.js 内部のファイル store ディレクトリ。 Authモジュールは、ユーザー認証ステータスとVuex状態内のユーザー詳細を auth 物体。 したがって、ユーザーがログインしているかどうかを確認できます。 this.$store.state.auth.loggedIn、どちらかが返されます true また false. 同様に、次のコマンドでユーザーの詳細を取得できます this.$store.state.auth.user、 どっちが null ログインしているユーザーがいない場合。

注: Authモジュールを使用して、ユーザー認証ステータスとユーザー詳細に直接アクセスすることもできます。 this.$auth.loggedInthis.$auth.user それぞれ。

計算されたプロパティをアプリの複数の場所で使用したい場合があるので、ストアゲッターを作成しましょう。

開ける index.js:

  1. nano store/index.js

そして、その中に以下のコードを貼り付けます。

store / index.js
export const getters = {
  isAuthenticated(state) {
    return state.auth.loggedIn
  },

  loggedInUser(state) {
    return state.auth.user
  }
}

ここでは、2つのゲッターを作成します。 最初の1つ (isAuthenticated)は、ユーザーの認証ステータスと2番目の(loggedInUser)詳細またはログインしたユーザーを返します。

次に、ゲッターを利用するようにNavbarコンポーネントを更新しましょう。 のコンテンツを置き換えます components/Navbar.vue 次のように:

コンポーネント/Navbar.vue
<template>
  <nav class="navbar is-light">
    <div class="container">
      <div class="navbar-brand">
        <nuxt-link class="navbar-item" to="/">Nuxt Auth</nuxt-link>
        <button class="button navbar-burger">
          <span></span>
          <span></span>
          <span></span>
        </button>
      </div>
      <div class="navbar-menu">
        <div class="navbar-end">
          <div class="navbar-item has-dropdown is-hoverable" v-if="isAuthenticated">
            <a class="navbar-link">
              {{ loggedInUser.username }}
            </a>
            <div class="navbar-dropdown">
              <nuxt-link class="navbar-item" to="/profile">My Profile</nuxt-link>
              <hr class="navbar-divider"/>
              <a class="navbar-item">Logout</a>
            </div>
          </div>
          <template v-else>
            <nuxt-link class="navbar-item" to="/register">Register</nuxt-link>
            <nuxt-link class="navbar-item" to="/login">Log In</nuxt-link>
          </template>
        </div>
      </div>
    </div>
  </nav>
</template>

<script>
import { mapGetters } from 'vuex'

export default {
  computed: {
    ...mapGetters(['isAuthenticated', 'loggedInUser'])
  }
}
</script>

スプレッド演算子を使用して、計算されたプロパティを作成します(...)からゲッターを抽出するには mapGetters. 次に使用する isAuthenticated、ユーザーメニューまたはリンクを表示します login また register ユーザーがログインしているかどうかによって異なります。 また、あなたは使用します loggedInUser 認証されたユーザーのユーザー名を表示します。

ここで、アプリを更新すると、次のようなものが表示されます。

ステップ7—ユーザーログインの処理

それでは、リピーターがログインできるようにしましょう。

新しいを作成します login.vue 内部のファイル pages ディレクトリ:

nano pages/login.vue

そして、その中に以下のコードを貼り付けます。

pages / login.vue
<template>
  <section class="section">
    <div class="container">
      <div class="columns">
        <div class="column is-4 is-offset-4">
          <h2 class="title has-text-centered">Welcome back!</h2>

          <Notification :message="error" v-if="error"/>

          <form method="post" @submit.prevent="login">
            <div class="field">
              <label class="label">Email</label>
              <div class="control">
                <input
                  type="email"
                  class="input"
                  name="email"
                  v-model="email"
                />
              </div>
            </div>
            <div class="field">
              <label class="label">Password</label>
              <div class="control">
                <input
                  type="password"
                  class="input"
                  name="password"
                  v-model="password"
                />
              </div>
            </div>
            <div class="control">
              <button type="submit" class="button is-dark is-fullwidth">Log In</button>
            </div>
          </form>
          <div class="has-text-centered" style="margin-top: 20px">
            <p>
              Don't have an account? <nuxt-link to="/register">Register</nuxt-link>
            </p>
          </div>
        </div>
      </div>
    </div>
  </section>
</template>

<script>
import Notification from '~/components/Notification'

export default {
  components: {
    Notification,
  },

  data() {
    return {
      email: '',
      password: '',
      error: null
    }
  },

  methods: {
    async login() {
      try {
        await this.$auth.loginWith('local', {
          data: {
          email: this.email,
          password: this.password
          }
        })

        this.$router.push('/')
      } catch (e) {
        this.error = e.response.data.message
      }
    }
  }
}
</script>

これは非常に似ています register ページ。 フォームには2つのフィールドが含まれています。 emailpassword. フォームが送信されると、 login メソッドが呼び出されます。 Authモジュールの使用 loginWith() ユーザーデータを渡すと、ユーザーはにログインします。 認証が成功した場合は、ユーザーをホームページにリダイレクトします。 それ以外の場合は、 error API応答から取得したエラーメッセージに。 ここでも、以前の通知コンポーネントを使用してエラーメッセージを表示しています。

ステップ8—ユーザープロファイルの表示

ログインしたユーザーが自分のプロファイルを表示できるようにしましょう。

新しいを作成します profile.vue 内部のファイル pages ディレクトリ:

  1. nano pages/profile.vue

そして、その中に以下のコードを貼り付けます。

pages / profile.vue
<template>
  <section class="section">
    <div class="container">
      <h2 class="title">My Profile</h2>
      <div class="content">
        <p>
          <strong>Username:</strong>
          {{ loggedInUser.username }}
        </p>
        <p>
          <strong>Email:</strong>
          {{ loggedInUser.email }}
        </p>
      </div>
    </div>
  </section>
</template>

<script>
import { mapGetters } from 'vuex'

export default {
  computed: {
    ...mapGetters(['loggedInUser'])
  }
}
</script>

をどのように使用しているかに注意してください loggedInUser ユーザーの詳細を表示するための以前のゲッター。

マイプロファイルリンクをクリックすると、マイプロファイルページが表示されます。

ステップ9—ユーザーのログアウト

Navbarコンポーネント内のログアウトリンクを更新します。

開ける Navbar.vue:

  1. nano components/Navbar.vue

使用するログアウトリンクを変更します @click="logout":

コンポーネント/Navbar.vue
// ...
<div class="navbar-dropdown">
  <nuxt-link class="navbar-item" to="/profile">My Profile</nuxt-link>
  <hr class="navbar-divider"/>
  <a class="navbar-item"  @click="logout">Logout</a>
</div>
// ...

ログアウトリンクをクリックすると、 logout 方法。

次に、を追加しましょう logout Navbarコンポーネントのスクリプトセクション内のメソッド:

コンポーネント/Navbar.vue
// ...

export default {
  // ...
  methods: {
    async logout() {
      await this.$auth.logout();
    },
  },
}

あなたは logout() Authモジュールの。 これにより、ユーザーのトークンがlocalstorageから削除され、ユーザーがホームページにリダイレクトされます。

ステップ10—プロファイルページを制限する

現在のところ、誰でも訪問できます profile ページ。 また、ユーザーがログインしていない場合は、エラーが発生します。

これを修正するには、プロファイルページをログインしているユーザーのみに制限する必要があります。 幸いなことに、Authモジュールを使用してそれを実現できます。 Authモジュールには auth このシナリオで使用できるミドルウェア。

それでは、を追加しましょう auth ミドルウェアから profile ページ。 を更新します script 以下のセクション:

pages / profile.vue
// ...

export default {
  middleware: 'auth',
  // ...
}

これで、ログインしていないユーザーがにアクセスしようとすると、 profile ページでは、ユーザーはにリダイレクトされます login ページ。

ステップ11—ゲストミドルウェアの作成

繰り返しになりますが、ログインしているユーザーであっても、ログインページと登録ページにアクセスできます。 これを修正する1つの方法は、ログインページと登録ページをログインしていないユーザーのみに制限することです。 これを行うには、ゲストミドルウェアを作成します。

内部 middleware ディレクトリ、新しいを作成します guest.js ファイル:

  1. nano middleware/guest.js

そして、その中に以下のコードを貼り付けます。

ミドルウェア/guest.js
export default function ({ store, redirect }) {
  if (store.state.auth.loggedIn) {
    return redirect('/')
  }
}

ミドルウェアは、最初の引数としてコンテキストを受け入れます。 だからあなたは抽出します storeredirect 文脈から。 次に、ユーザーがログインしているかどうかを確認してから、ユーザーをホームページにリダイレクトします。 それ以外の場合は、リクエストの通常の実行を許可します。

次に、このミドルウェアを活用しましょう。 を更新します script 両方のセクション loginregister 以下のように:

pages/login.vueおよびpages/register.vue
// ...

export default {
  middleware: 'guest',
  // ...
}

これで、すべてが期待どおりに機能します。

結論

このチュートリアルでは、Authモジュールを使用してNuxt.jsアプリケーションに認証を実装する方法を説明しました。 また、ミドルウェアを利用して認証フローをスムーズに保つ方法も学びました。

Authモジュールの詳細については、docsをご覧ください。

Vue.jsの詳細については、Vue.jsトピックページで演習とプログラミングプロジェクトを確認してください。

モバイルバージョンを終了