前提・実現したいこと
vue-routerのナビゲーションガードを使用して、認証が必要なページへのアクセスの制限を実装しました。
しかし、ブラウザの新しいタブを開いてURLから認証が必要なページへアクセスすると、アクセスできてしまうという落とし穴を見つけてしまいました。
例えば、私のアプリでは、ログインしている場合はユーザーの新規登録ページ、ログインページにはアクセスできないようにしたいです。しかし、ログインしている状態で、他のページからログインページにアクセスするとナビゲーションガードが実行されて、アクセスできないのですが、ブラウザの新しいタブを開いてurlからログインページにアクセスするとできてしまいます。
この問題を解決したいです。
フォルダ構成
app/javascript ├── app.vue ├── channels │ ├── consumer.js │ └── index.js ├── components │ ├── Flash.vue │ ├── Header.vue │ ├── Home.vue │ └── users │ ├── LogInPage.vue │ └── SignUpPage.vue ├── packs │ ├── application.js │ ├── hello_vue.js │ ├── modules │ │ └── validate_email.js │ ├── plugins │ └── csrf_token_plugin.js │ ├── router.js └── store └── store.js
該当のソースコード
以下、app/javascriptを@で記します。
・@/router.js
meta-requiresAuthは、trueなら認証を必要とする、falseなら認証済みではアクセスできない、nullならどちらでも良い、という仕様です。
js
1import Vue from 'vue' 2import store from './store/store' 3import Router from 'vue-router' 4import Home from './components/Home' 5 6Vue.use(Router) 7 8// ルーティング 9const routes = [ 10 // トップページ 11 { 12 path: '/', 13 component: Home, 14 meta: { 15 title: '', // ページ固有のタイトル 16 requiresAuth: null // 認証が必要か? 17 } 18 }, 19 // ユーザー新規登録ページ 20 { 21 path: '/sign_up', 22 component: () => import('./components/users/SignUpPage'), 23 meta: { 24 title: 'アカウント登録', 25 requiresAuth: false 26 } 27 }, 28 // ログインページ 29 { 30 path: '/log_in', 31 component: () => import('./components/users/LogInPage'), 32 meta: { 33 title: 'ログイン', 34 requiresAuth: false 35 } 36 } 37] 38 39const router = new Router({ 40 routes 41}) 42 43// ナビゲーションガード 44router.beforeEach((to, from, next) => { 45 // 認証が必要なページで、ログインしていない場合 46 if (to.meta.requiresAuth && !store.getters.isLoggedIn) { 47 store.dispatch('setFlashMessage', 'このページにアクセスするにはログインする必要があります') 48 next('/log_in') 49 // 認証済みではアクセスできないページで、ログイン済みの場合 50 } else if (to.meta.requiresAuth === false && store.getters.isLoggedIn) { 51 store.dispatch('setFlashMessage', 'このページにはアクセスできません') 52 next(false) 53 // ログインしていても、してなくても大丈夫なページの場合 54 } else { 55 next() 56 } 57}) 58 59export default router 60
・@/store/store.js
js
1import Vue from "vue" 2import Vuex from 'vuex' 3 4Vue.use(Vuex) 5 6export default new Vuex.Store({ 7 strict: true, 8 state: { 9 flashMessage: '', // flashの文章 10 currentUser: {} // ログイン中のユーザ 11 }, 12 getters: { 13 getFlashMessage(state) { 14 return state.flashMessage 15 }, 16 // ユーザーがログインしているか? 17 isLoggedIn(state) { 18 return Object.keys(state.currentUser).length !== 0 ? true : false 19 } 20 }, 21 mutations: { 22 // flashメッセージを設定 23 setFlashMessage(state, message) { 24 state.flashMessage = message 25 }, 26 // ログイン中のユーザを設定 27 setCurrentUser(state, user) { 28 state.currentUser = user 29 } 30 }, 31 actions: { 32 setFlashMessage(context, message) { 33 context.commit('setFlashMessage', message) 34 }, 35 setCurrentUser(context, user) { 36 context.commit('setCurrentUser', user) 37 } 38 } 39}) 40
・@/app.vue
vue
1<template> 2 <div id="app"> 3 <Header></Header> 4 <Flash></Flash> 5 <router-view></router-view> 6 </div> 7</template> 8 9<script> 10import axios from 'axios' 11import Header from './components/Header' 12import Flash from './components/Flash' 13 14export default { 15 components: { 16 Header, 17 Flash 18 }, 19 methods: { 20 // ページのタイトルを返すメソッド 21 setTitle(routeInstance) { 22 const baseTitle = 'アプリ名' 23 let pageTitle = routeInstance.meta.title 24 if (routeInstance.meta.title) { 25 document.title = pageTitle + ' | ' + baseTitle 26 } else { 27 document.title = baseTitle 28 } 29 } 30 }, 31 mounted() { 32 this.setTitle(this.$route) // ページタイトルを設定 33 // マウント時にストアのログイン中のユーザを設定 34 35 axios.get('/api/logged_in') 36 .then(res => { 37 let user = res.data 38 if (Object.keys(user).length !== 0) { 39 this.$store.dispatch('setCurrentUser', user) 40 } else { 41 this.$store.dispatch('setCurrentUser', {}) 42 } 43 }) 44 .catch(err => { 45 console.log(err) 46 }) 47 }, 48 watch: { 49 // ページが遷移するたびにページタイトルを変更 50 '$route'(to, from) { 51 this.setTitle(to) 52 } 53 } 54} 55</script> 56 57<style scoped> 58p { 59 font-size: 2em; 60 text-align: center; 61} 62</style>
試したこと
@/app.vue のライフサイクルフックを使用してアクセス制限を試みたがだめでした。
vue
1mounted() { 2 this.setTitle(this.$route) // ページタイトルを設定 3 // マウント時にストアのログイン中のユーザを設定 4 let user = {} 5 axios.get('/api/logged_in') 6 .then(res => { 7 user = res.data 8 if (Object.keys(user).length !== 0) { 9 this.$store.dispatch('setCurrentUser', user) 10 } else { 11 this.$store.dispatch('setCurrentUser', {}) 12 } 13 }) 14 .catch(err => { 15 console.log(err) 16 }) 17 // ここから追加 18 // 認証が必要なページで、ログインしていない場合 19 if (this.$route.meta.requiresAuth && Object.keys(user).length === 0) { 20 this.$store.dispatch('setFlashMessage', このページにアクセスするにはログインする必要があります) 21 this.$route.push('/log_in') 22 // ログイン済みではアクセスできないページで、ログイン済みの場合 23 } else if (this.$route.meta.requiresAuth === false && Object.keys(user).length !== 0) { 24 this.$store.dispatch('setFlashMessage', 'このページにはアクセスできません') 25 this.$route.push('/') 26 } 27 },
あなたの回答
tips
プレビュー