Nuxt.jsのSSR・SPA時のフロント側の認証ビューを自前で実装する
技術スタック JavascriptNuxt.js

Nuxt.jsのSSR・SPA時のフロント側の認証ビューを自前で実装する

2022.05.19

こんにちは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どちらも以下の通りです。

  1. Nuxt.js側でユーザーの情報を保存する。
  2. ログインの是非はユーザー情報の有無で判断する。
  3. 何かしらのタイミングでユーザー情報を取得するAPIを都度発行する。

Storeの調整

ではまずStoreにて以下のようにuserステートを作成します。

store/index.js
export const state = () => ({
    user:null,
});

デフォルトではnullにしておきます。このuserステートがnullでなく、ユーザー情報のオブジェクトである場合をログイン状態とします。mutationなどでこのステートに値をセットできるように作っておきます。

store/index.js
export const mutations = {
    setUser(state,{user}){
            state.user = user;
    }
}

ミドルェアの作成

次に「ログインしたユーザーのみがアクセス可能なページ」を実装できるようにミドルウェアを実装します。middleware/auth.jsを作成します。

middleware/auth.js
export default function ({ store, redirect }) {
    if (!store.state.user) {
        redirect('/login');
    }
}

単純にStoreのUserステートがnullかどうかでログインページに飛ばすようにしています。

ページコンポーネントでは以下のようにしてミドルウェアを有効にします。

pages/auth/index.vue
<template>
    <div>
        auth
    </div>
</template>
<script>
export default {
    name:"home",
    middleware:"auth",
}
</script>

こうすることでログインが必要なページを実装することができます。またはnuxt.config.js にて以下のように設定することで全てのページに認証ミドルウェアを適用できます。

nuxt.config.js
router: {
    middleware: 'auth',
},

特定のページ、ディレクトリを除く場合

nuxt.config.js にてグローバルな認証ミドルウェアを実装できますが、未ログインでもアクセス可能なページや、未ログインでないと閲覧できないページ(ログインページなど)では不便です。

個別にミドルウェアを作成してページごとに設定してオーバーライドすることも可能ですが、私はよく以下のようにauth.js実装しています。

middleware/auth.js
// 特定のページの認証を外す
export default function ({ store, redirect }) {
    if (!store.state.user && route.fullPath !== '/login') {
        redirect('/login');
    }
}
middleware/auth.js
// 特定のディレクトリ配下を外す
export default function ({ store, redirect }) {
    if (!store.state.user && route.fullPath.indexOf('/public') != -1) {
        redirect('/login');
    }
}

meリクエストを初期処理にいれる。

storeとミドルェアの準備ができたので、APIサーバーに通信をしてユーザー情報を取得するメソッドを作成しておきます。ここではmeリクエストと呼ぶことにします。今回はaxiosでhttps://api.example.com/auth/meへトークンと一緒にリクエストするとユーザー情報のJSONがレスポンスとして得られるとします。期限切れやトークンがない場合や間違っている場合などは401がレスポンスとして戻ります。

SPA

SPAの場合はnuxt-client-init-moduleというライブラリ を使用するとスムーズです。SSRの場合はNuxtServiceInitという初期処理を実装できるフックがあるのですが、SPAの場合はそれがありあません。Pluginでできなくもないのですが、nuxt-client-init-moduleを使用するとうまくいきやすいです。

上記のライブラリをインストールしてstoreにてmeリクエストをします。

store/index.js]
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

SSRの場合は今度はNuxtServiceInitに変更するだけです。これは特にライブラリは必要なく、SSRであれば利用できます。

store/index.js]
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のままになります。

完了

エッセンスは以下の通りです。

  1. 初期処理にユーザー情報を取得するAPIをリクエスト
  2. 認証済みであればstoreのuserにユーザー情報のオブジェクトが入る
  3. ミドルウェアやstoreの情報を使用して認証による分岐を行う
Copyright © 2021 jun. All rights reserved.