vue.jsで認証機能を実装する

Vue.jsのrouter機能を使ってユーザ認証を実装してみます。

##環境
| カテゴリ       | バージョンなど       |
|:-----------------|------------------:|
| os               |windows 10 home 64bit |
| vue               |2.4.2 |
| vue-router      |2.7.0 |

##全体のhtml
```index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Vue.js SPAのサンプルアプリケーション</title>
    <style>
      .container {
        width:100%;
        height:100%;
        display:flex;
        justify-content: center;
        align-items: center;
      }
      .content {
        min-width: 33%;
        min-height: 33%;
      }
      .error {
        color: red;
      }
    </style>
  </head>
  <body>

<div id="app">
  <div class="container">
    <div class="content">
      <router-view></router-view>
    </div>
  </div>
</div>

<script src="https://unpkg.com/vue@2.4.2/dist/vue.min.js"></script>
<script src="https://unpkg.com/vue-router@2.7.0/dist/vue-router.min.js"></script>
<script>

//認証情報はこのオブジェクトで管理する。
var Auth = {
  loggedIn: false,
  login: function(email, pass) { 
    //TODO: ここはサーバで認証処理(login)を実行する。
    (email == "vue@example.com" && pass == "vue" )?this.loggedIn = true:this.loggedIn = false;
    //TODO: サーバから取得したtokenをlocalstrageに保存するように変更が必要。
    localStorage.token = Math.random().toString(36).substring(7)
    return this.loggedIn;
  },
  logout: function() { 
    //TODO: ここはサーバで認証処理(logout)を実行する。
    this.loggedIn = false 
    delete localStorage.token
  }
};

//トップメニューコンポーネント
var Top = {
  template: '<div>'
            +'<h2>Top</h2>'
            +'<div style="width:600px;text-align:right;">'
            +'<router-link to="/logout">Logout</router-link>'
            +'</div>'
            +'<ul>'
            +'<li>'
            +'メニュー１'
            +'</li>'
            +'<li>'
            +'メニュー２'
            +'</li>'
            +'</ul>'
            +'</div>'
};

//ログインコンポーネント
var Login = {
  template: ''
      +'<div>'
      +'  <h2>Login</h2>'
      +'  <p v-if="$route.query.redirect">'
      +'    ログインしてください'
      +'  </p>'
      +'  <p v-if="error" class="error">ログインに失敗しました</p>'
      +'  <form @submit.prevent="login">'
      +'  <table>'
      +'    <tr><td>E-Mail:</td><td><input v-model="email" placeholder="email"></td></tr>'
      +'    <tr><td>password:</td><td><input v-model="pass" placeholder="password" type="password"></td></tr>'
      +'  </table>'
      +'  <input type="submit" value="Login" v-on:click="login">'
      +'  </form>'
      +'</div>',
  data: function () {
    return {
      email: 'vue@example.com',
      pass: '',
      error: false
    }
  },
  methods: {
    login: function() {
      this.error = !Auth.login(this.email, this.pass);
      
      if (!this.error) {
        router.push(this.$route.query.redirect);
      };
    }
  }
};

//ログアウトコンポーネント
//画面描画時に、ログアウトメソッドが呼び出され、ログアウトする。
var Logout = {
  template: '<input type="submit" value="Logout" v-on:click="logout">',
  methods: {
    logout: function() {
      Auth.logout();
      router.push('/top');
    }
  },
  created: function(){
    this.logout()
  }    
};

//ルーター定義
var routes = [
  { path: '/top', component: Top, meta: { requiresAuth: true }},
  { path: '/login', component: Login },
  { path: '/logout', component: Logout },
  {
    // 定義されていないパスへの対応。トップページへリダイレクトする。
    path: '*',
    redirect: '/top'
  }  
];

//ルーター
var router = new VueRouter({
  "routes": routes
});

//認証がない場合は、ログインページに遷移する。
router.beforeEach(function (to, from, next) {
  if (to.matched.some(function (record) {
    return record.meta.requiresAuth;
  }) && !Auth.loggedIn) {
    next({ path: '/login', query: { redirect: to.fullPath } });
  } else {
    next();
  }
});

var app = new Vue({
  el: '#app',
  "router": router
});
</script>
</body>
</html>
```

Python+Djangoやphp+laravelなどのフルスタックだと認証機能があるのですが、そうではない環境では、vue-routerを使うというのも一つの手段として有効ではないかと思います。