Nuxt.jsアプリで認証を実装する方法
序章
このチュートリアルでは、Nuxt.jsアプリで認証を実装します。 Auth
モジュール。
このチュートリアルでは、以下を使用します。 JWT
認証用。
以下は、このチュートリアルで構築するものの簡単なデモです。
このアプリケーションのソースコードはGitHubにあります。
警告:このチュートリアルのいくつかのパッケージには、既知の脆弱性を持つ依存関係が含まれています。 本番環境では、これらのパッケージをアップグレードするか、代替手段を見つけるか、パッチが適用された修正を含むフォークバージョンを作成することで、これらの問題を解決します。 ただし、チュートリアルの限られたコンテキスト内では、そのままの教育的価値を提供します。
前提条件
このチュートリアルを完了するには、次のものが必要です。
- Node.jsはローカルにインストールされます。これは、Node.jsのインストール方法とローカル開発環境の作成に従って実行できます。
- APIのクローンを作成するには、オプションで有効なGitのインストールが必要です。Gitの使用開始を参照してください。
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は以下を利用します:
- 認証用のJWT( JSON Web Tokens )
- SQLite
- CORSが有効
APIには3つのエンドポイントがあります。
/register
:ユーザー登録のエンドポイント/login
:ユーザーを認証するためのエンドポイント/me
:現在認証されているユーザーの詳細を取得するためのエンドポイントであり、auth
ミドルウェア。これは、エンドポイントにアクセスするにはユーザーを認証する必要があることを意味します
まず、ターミナルウィンドウで次のコマンドを実行します。
- git clone https://github.com/do-community/jwt-auth-api.git
次に、プロジェクトディレクトリに移動します。
- cd jwt-auth-api
そして、APIの依存関係をインストールします。
- 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 undefined
と Error: Cannot find module '[...]/node_modules/sqlite3/lib/binding/[...]/node_sqlite3.node'
.
次に、名前を変更します .env.example
に .env
:
- mv .env.example .env
そして、を生成します APP_KEY
:
- npx @adonisjs/cli@4.0.12 key:generate
君は見るべきだ:
- Outputgenerated: unique APP_KEY
それが完了したら、移行を実行してみましょう。
- npx @adonisjs/cli@4.0.12 migration:run
これで、APIを開始できます。
- # ensure that you are in the `jwt-auth-api` project directory
- npm start
APIにアクセスできます http://127.0.0.1:3333/api
. チュートリアルの残りの期間は、これをターミナルウィンドウで実行したままにします。
ステップ2—Nuxt.jsアプリを作成する
これで、Nuxt.jsアプリを作成できます。 新しいターミナルウィンドウを開いて使用する vue-cli
Nuxtスターターテンプレートを使用して新しいVueプロジェクトを初期化するには:
- npx [email protected] init nuxt/starter nuxt-auth
注:テスト時、 vue-cli
非推奨です。 @vue/cli
Vueプロジェクトの現在のコマンドラインツールです。 と @vue/cli-init
レガシーに推奨されるアプローチです vue-cli
プロジェクト。 でも、 create-nuxt-app
現代のNuxtプロジェクトに推奨されるアプローチです。
次に、プロジェクトディレクトリに移動する必要があります。
- cd nuxt-auth
そして、依存関係をインストールします。
npm install
次に、アプリを起動できます。
- npm run dev
アプリはで実行されている必要があります http://localhost:3000
. Webブラウザーでアプリケーションを表示して、によって作成されたデフォルトのVueアプリケーションを確認できます。 vue-cli
.
ステップ3—必要なNuxt.jsモジュールをインストールする
それでは、アプリに必要なNuxt.jsモジュールをインストールしましょう。 NuxtAuthモジュールとNuxtAxiosモジュールを使用することになります。 auth
モジュールは内部でAxiosを利用します:
- # ensure that you are in the `nuxt-auth` project directory
- npm install @nuxtjs/auth@4.5.1 @nuxtjs/axios@5.3.1 --save
それが完了したら、開きます nuxt.config.js
:
- nano 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
:
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の戦略に対応する戦略:
- 認証が成功すると、トークンは応答で次のように利用可能になります。
token
内部のオブジェクトdata
物体。 - 同様に、からの応答
/me
エンドポイントは内部になりますdata
物体。 - 最後に、
logout
にfalse
APIにはログアウト用のエンドポイントがないためです。 ユーザーがログアウトすると、localStorageからトークンを削除するだけです。
ステップ4—ナビゲーションバーコンポーネントを作成する
アプリのスタイルを設定するには、ブルマを利用できます。
開ける nuxt.config.js
以下のコードを link
内部にあるオブジェクト head
物体:
module.exports = {
// ...
head: {
// ...
link [
// ...
{
rel: 'stylesheet',
href: 'https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.1/css/bulma.min.css'
}
]
},
// ...
}
それでは、Navbarコンポーネントを作成しましょう。
- nano components/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
:
- nano layouts/default.vue
そして、コンテンツを次のように置き換えます。
<template>
<div>
<Navbar/>
<nuxt/>
</div>
</template>
<script>
import Navbar from '~/components/Navbar'
export default {
components: {
Navbar
}
}
</script>
また、ホームページを更新しましょう。
開ける index.vue
:
- nano 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
ファイル:
- nano 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
:
- nano components/Notifaction.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.loggedIn
と this.$auth.user
それぞれ。
計算されたプロパティをアプリの複数の場所で使用したい場合があるので、ストアゲッターを作成しましょう。
開ける index.js
:
- nano 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
次のように:
<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
そして、その中に以下のコードを貼り付けます。
<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つのフィールドが含まれています。 email
と password
. フォームが送信されると、 login
メソッドが呼び出されます。 Authモジュールの使用 loginWith()
ユーザーデータを渡すと、ユーザーはにログインします。 認証が成功した場合は、ユーザーをホームページにリダイレクトします。 それ以外の場合は、 error
API応答から取得したエラーメッセージに。 ここでも、以前の通知コンポーネントを使用してエラーメッセージを表示しています。
ステップ8—ユーザープロファイルの表示
ログインしたユーザーが自分のプロファイルを表示できるようにしましょう。
新しいを作成します profile.vue
内部のファイル pages
ディレクトリ:
- nano 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
:
- nano components/Navbar.vue
使用するログアウトリンクを変更します @click="logout"
:
// ...
<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コンポーネントのスクリプトセクション内のメソッド:
// ...
export default {
// ...
methods: {
async logout() {
await this.$auth.logout();
},
},
}
あなたは logout()
Authモジュールの。 これにより、ユーザーのトークンがlocalstorageから削除され、ユーザーがホームページにリダイレクトされます。
ステップ10—プロファイルページを制限する
現在のところ、誰でも訪問できます profile
ページ。 また、ユーザーがログインしていない場合は、エラーが発生します。
これを修正するには、プロファイルページをログインしているユーザーのみに制限する必要があります。 幸いなことに、Authモジュールを使用してそれを実現できます。 Authモジュールには auth
このシナリオで使用できるミドルウェア。
それでは、を追加しましょう auth
ミドルウェアから profile
ページ。 を更新します script
以下のセクション:
// ...
export default {
middleware: 'auth',
// ...
}
これで、ログインしていないユーザーがにアクセスしようとすると、 profile
ページでは、ユーザーはにリダイレクトされます login
ページ。
ステップ11—ゲストミドルウェアの作成
繰り返しになりますが、ログインしているユーザーであっても、ログインページと登録ページにアクセスできます。 これを修正する1つの方法は、ログインページと登録ページをログインしていないユーザーのみに制限することです。 これを行うには、ゲストミドルウェアを作成します。
内部 middleware
ディレクトリ、新しいを作成します guest.js
ファイル:
- nano middleware/guest.js
そして、その中に以下のコードを貼り付けます。
export default function ({ store, redirect }) {
if (store.state.auth.loggedIn) {
return redirect('/')
}
}
ミドルウェアは、最初の引数としてコンテキストを受け入れます。 だからあなたは抽出します store
と redirect
文脈から。 次に、ユーザーがログインしているかどうかを確認してから、ユーザーをホームページにリダイレクトします。 それ以外の場合は、リクエストの通常の実行を許可します。
次に、このミドルウェアを活用しましょう。 を更新します script
両方のセクション login
と register
以下のように:
// ...
export default {
middleware: 'guest',
// ...
}
これで、すべてが期待どおりに機能します。
結論
このチュートリアルでは、Authモジュールを使用してNuxt.jsアプリケーションに認証を実装する方法を説明しました。 また、ミドルウェアを利用して認証フローをスムーズに保つ方法も学びました。
Authモジュールの詳細については、docsをご覧ください。
Vue.jsの詳細については、Vue.jsトピックページで演習とプログラミングプロジェクトを確認してください。