Vue.js & TypeScript でNavigationGuardを実装する

こんにちは!kossyです!




さて、今回はVue.js & TypeScriptでのRouterGuardの実装手順をブログに残してみたいと思います。

なお、今回はRails APIモードでdevise_token_authを使ってtoken認証を行っていて、VueRouterの導入は済んでいることを前提として書かせていただきます。



環境

Vue.js 3.0.5
Vue CLI 4.5.9
node 14.15.0
npm 6.14.8

Ruby 2.6.6
Rails 6.0.3.5
devise_token_auth 1.1.3



実装手順

まずはsrc/router/index.tsのルーティング定義にmeta情報を付与します。

export default [
  {
    path: '/',
    name: 'home',
    component: Home,
    meta: { requiresAuth: true }
  }
}

ルーとパスにmeta情報としてrequiresAuthを付与しています。
後に実装するguards.ts内でrequiresAuthがtrueの場合に処理を実行するために用います。



次に、src/router/guards.tsを作成します。

import { validateToken } from '@/api/auth'
import { NavigationGuardNext, RouteLocationNormalized, RouteRecordNormalized } from 'vue-router'

export const authorizeToken = (to: RouteLocationNormalized, from: RouteLocationNormalized, next: NavigationGuardNext) => {
  if (to.matched.some((record: RouteRecordNormalized) => record.meta.requiresAuth)) {
    validateToken()
      .then(() => {
        next()
      })
      .catch(() => {
        next({ path: '/login' })
      })
  } 
}

recordの中身はRouteRecordNormalizedインターフェイスとなっており、pathやnameやmetaといったプロパティが定義されています。
なので、record.meta.requiresAuthでroutes.tsの方で定義したmeta情報にアクセスできます。

requiresAuthがtrueの場合、validateToken関数を呼び出し、エラーなく処理されればnext関数を引数なしで呼び出し、
認証に失敗した場合等の事象があればログインページに遷移するようになっています。

validateToken関数の中身はこのような実装になっています。(あくまで例です。)

export const getAuthDataFromStorage = (): AuthHeaders => {
  return {
    'access-token': localStorage.getItem('access-token'),
    client: localStorage.getItem('client'),
    expiry: localStorage.getItem('expiry'),
    uid: localStorage.getItem('uid'),
    'Content-Type': 'application/json'
  }
}

export const setAuthDataFromResponse = (authData: AuthHeaders): void => {
  if (authData['access-token'] && authData.client && authData.uid && authData.expiry) {
    localStorage.setItem('access-token', authData['access-token'])
    localStorage.setItem('client', authData.client)
    localStorage.setItem('uid', authData.uid)
    localStorage.setItem('expiry', authData.expiry)
  }
}

export const removeAuthDataFromStorage = (): void => {
  localStorage.removeItem('access-token')
  localStorage.removeItem('client')
  localStorage.removeItem('uid')
  localStorage.removeItem('expiry')
}

export const validateToken = async () => {
  return await axios.get(process.env.VUE_APP_API_BASE  + '/auth/validate_token', { headers: getAuthDataFromStorage() })
    .then((response) => {
      setAuthDataFromResponse(response.headers)
      return response.data
    })
    .catch(() => {
      removeAuthDataFromStorage()
    })
}

トークンの有効性を検証するURLにリクエストを送って、トークンが有効であればユーザー情報を返却されます。
認証に失敗した場合はLocalStorageからトークンを削除します。



次に、index.tsを編集します。

import { authorizeToken } from './guards'

// 省略

router.beforeEach(authorizeToken)

routerインスタンスのbeforeEach関数の引数に、先ほど定義したauthorizeToken関数を渡すと、ページ遷移が発生するたびにauthorizeToken関数が呼び出されるようになります。




勉強になりました。



大いに参考にさせていただいたサイト

ナビゲーションガード | Vue Router