こんにちはjunです。webアプリを作成する時は大体、認証機能をつけることが多いです。LaravelやDjangoなどでは一発で行けますが、Nuxt.js、Vue.jsを使用したSPA、Node.jsでのSSRコンテンツを作成する場合は一捻り必要です。
Nuxt.jsなどで作成するアプリはバックエンドと独立し、都度バックエンドへのAPI通信の際にトークンを渡したりすることで認証が必要なAPIへアクセスしています。
ビュー側の処理でも
といった処理が行われることが多いです。この記事では上記のような認証を用いたビューの表現、ルーターの設定をNuxt.jsでどう行うかの解説をしたいと思います。SPA(クライアントサイドレンダリング)とSSR(サーバーサイドレンダリング)の2パターンを実装します。ちなみにnuxt-authなどのライブラリは使用しません。
また、ログインメソッドの処理やリクエストヘッダにどうこうするとか、バックエンド側の処理については今回は解説しません。あくまでフロント側(Nuxt.js側)の表示やロジックに認証が必要な場合にどう実装するかについて解説するのみです。
まずこの実装を行うにあたりJWTやクッキーなどなんらかの認証トークンが取得できていること、またそれらの値を使用できることを前提として進めます。
LaravelやDjangoなどではリクエストを送る際にヘッダーにセッションIDを含むクッキーを送信することで、サーバー側でログインによるビューやロジックの分岐を行っています。
しかしNuxt.jsでSPAの場合はコンテンツをクライアント側で生成し、またSSRの場合はnode.jsのサーバーで行われます。すなわちセッションやユーザー情報を保存しているAPIサーバーから「どうにかしてNuxt.js側にユーザー情報を渡す」必要があります。
これから実装する内容はSSR、SPAどちらも以下の通りです。
ではまずStoreにて以下のようにuserステートを作成します。
export const state = () => ({
user:null,
});
デフォルトではnullにしておきます。このuserステートがnullでなく、ユーザー情報のオブジェクトである場合をログイン状態とします。mutationなどでこのステートに値をセットできるように作っておきます。
export const mutations = {
setUser(state,{user}){
state.user = user;
}
}
次に「ログインしたユーザーのみがアクセス可能なページ」を実装できるようにミドルウェアを実装します。middleware/auth.js
を作成します。
export default function ({ store, redirect }) {
if (!store.state.user) {
redirect('/login');
}
}
単純にStoreのUserステートがnullかどうかでログインページに飛ばすようにしています。
ページコンポーネントでは以下のようにしてミドルウェアを有効にします。
<template>
<div>
auth
</div>
</template>
<script>
export default {
name:"home",
middleware:"auth",
}
</script>
こうすることでログインが必要なページを実装することができます。またはnuxt.config.js
にて以下のように設定することで全てのページに認証ミドルウェアを適用できます。
router: {
middleware: 'auth',
},
nuxt.config.js
にてグローバルな認証ミドルウェアを実装できますが、未ログインでもアクセス可能なページや、未ログインでないと閲覧できないページ(ログインページなど)では不便です。
個別にミドルウェアを作成してページごとに設定してオーバーライドすることも可能ですが、私はよく以下のようにauth.js
実装しています。
// 特定のページの認証を外す
export default function ({ store, redirect }) {
if (!store.state.user && route.fullPath !== '/login') {
redirect('/login');
}
}
// 特定のディレクトリ配下を外す
export default function ({ store, redirect }) {
if (!store.state.user && route.fullPath.indexOf('/public') != -1) {
redirect('/login');
}
}
storeとミドルェアの準備ができたので、APIサーバーに通信をしてユーザー情報を取得するメソッドを作成しておきます。ここではmeリクエストと呼ぶことにします。今回はaxiosでhttps://api.example.com/auth/me
へトークンと一緒にリクエストするとユーザー情報のJSONがレスポンスとして得られるとします。期限切れやトークンがない場合や間違っている場合などは401がレスポンスとして戻ります。
SPAの場合はnuxt-client-init-moduleというライブラリ を使用するとスムーズです。SSRの場合はNuxtServiceInitという初期処理を実装できるフックがあるのですが、SPAの場合はそれがありあません。Pluginでできなくもないのですが、nuxt-client-init-moduleを使用するとうまくいきやすいです。
上記のライブラリをインストールしてstoreにてmeリクエストをします。
export const actions = {
async nuxtClientInit({ commit }, context) {
await this.$axios.get('https://api.example.com/auth/me')
.then(async res=>{
commit('setUser', {user:res.data});
})
.catch(err=>{
console.error(err)
})
}
}
nuxtClientInitを使用することでページ側の初期化処理より前にユーザー情報を取得できます。
SSRの場合は今度はNuxtServiceInit
に変更するだけです。これは特にライブラリは必要なく、SSRであれば利用できます。
export const actions = {
async NuxtServiceInit({ commit }, context) {
// サーバーサイドでトークンを入れるなどが必要な場合は適宜入れください。
await this.$axios.get('https://api.example.com/auth/me')
.then(async res=>{
commit('setUser', {user:res.data});
})
.catch(err=>{
console.error(err)
})
}
}
NuxtServiceInitというサーバーサイドで上記のリクエストを行って、storeにユーザー情報をいれてくれます。401が来てもcatchしてくれ、ユーザー情報はnullのままになります。
エッセンスは以下の通りです。