JWTのまたはJSONWebトークンは、ステートフルサーバーを必要とせずに、検証可能なセッション状態をクライアントに安全に保存するための一般的な方法です。 これらは、「サーバーレス」Webアプリケーションの台頭とともに、最近非常に人気が高まっています。 JWTはアプリケーションの状態の中核部分ですが、トークンであり、解析可能なデータの一部でもあります。 では、どのように両方の方法でそれらを使用するのでしょうか? Vue.jsでJWTを簡単に操作できるいくつかのパターンを次に示します。

このガイド全体を通して、GET http://localhost/vuejs-jwt-example/auth?u=username&p=passwordで文字列としてJWTで応答するAPIエンドポイントがあるふりをします。 これを独自の実装に置き換えたいと思うでしょう。

セッション間でJWTを永続化する方法はあなたに任されています。機密データをlocalStorageに保存することの危険性に注意してください!

Vuexなし

おそらく、私が行う最も重要な推奨事項は、解析されたバージョンのJWTを決して保存しないことです。 文字列と解析されたオブジェクトの両方を別々に保存することは、苦痛の世界に身を置くことになります。

代わりに、Vue.jsの計算されたプロパティを使用して、文字列が更新されるたびに文字列からオンデマンドでオブジェクトを作成します。

基本的なVue.jsコンポーネントを使用すると、次のようになります。

<template>
  <div>
    <p>JWT: {{jwt}}</p>
    <p>User ID: {{jwtData.sub}}</p>
    <p>Issuer: {{jwtData.iss}}</p>
    <button @click.native="doSomethingWithJWT()">Do Something</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      jwt: ''
    }
  },

  computed: {
    // this.jwtData will update whenever this.jwt changes.
    jwtData() {
      // JWT's are two base64-encoded JSON objects and a trailing signature
      // joined by periods. The middle section is the data payload.
      if (this.jwt) return JSON.parse(atob(this.jwt.split('.')[1]));
      return {};
    }
  },

  methods: {
    async fetchJWT() {
      // Error handling and such omitted here for simplicity.
      const res = await fetch(`http://localhost/vuejs-jwt-example/auth?u=username&p=password`);
      this.jwt = await res.text();
    },

    async doSomethingWithJWT() {
      const res = await fetch(`http://localhost/vuejs-jwt-example/do-something`, {
        method: 'POST',
        headers: new Headers({
          Authorization: `Bearer: ${this.jwt}`
        })
      });
      // Do stuff with res here.
    }
  },

  mounted() {
    this.fetchJWT();
  }
}
</script>

Vuexで

Vuex を使用している場合は、Vuexアクションとゲッターに基づいた同様のパターンを使用できます。

これは、user vuexモジュールの例です。これを使用すると、JWTをフェッチして、文字列とオブジェクトの両方の形式でアクセスできます。

export const UserModule = {
  state: {
    currentJWT: ''
  },

  getters: {
    jwt: state => state.currentJWT,
    jwtData: (state, getters) => state.currentJWT ? JSON.parse(atob(getters.jwt.split('.')[1])) : null,
    jwtSubject: (state, getters) => getters.jwtData ? getters.jwtData.sub : null,
    jwtIssuer: (state, getters) => getters.jwtData ? getters.jwtData.iss : null
  },

  mutations: {
    setJWT(state, jwt) {
      // When this updates, the getters and anything bound to them updates as well.
      state.currentJWT = jwt;
    }
  }

  actions: {
    async fetchJWT ({ commit }, { username, password }) {
      // Perform the HTTP request.
      const res = await fetch(`http://localhost/vuejs-jwt-example/auth?u=${username}&p=${password}`);
      // Calls the mutation defined to update the state's JWT.
      commit('setJWT', await res.text());
    },
  }
}

これは、上記で書いたものと同様のコンポーネントで次のように使用できます。

<template>
  <div>
    <p>JWT: {{jwt}}</p>
    <p>User ID: {{jwtSubject}}</p>
    <p>Issuer: {{jwtIssuer}}</p>
    <button @click.native="doSomethingWithJWT()">Do Something</button>
  </div>
</template>

<script>
import { mapGetters, mapActions } from 'vuex';

export default {
  computed: {
    ...mapGetters([
      'jwt',
      'jwtSubject',
      'jwtIssuer'
    ])
  },

  methods: {
    ...mapActions([
      `fetchJWT`
    ]),

    // The implementation here doesn't change at all!
    async doSomethingWithJWT() {
      const res = await fetch(`http://localhost/vuejs-jwt-example/do-something`, {
        method: 'POST',
        headers: new Headers({
          Authorization: `Bearer: ${this.jwt}`
        })
      });
      // Do stuff with res here.
    }
  },

  mounted() {
    this.fetchJWT({
      // #Security...
      username: 'username',
      password: 'password'
    });
  }
}
</script>

ここに示すアプローチの利点は、JWTiselfが文字列形式でのみ保存および更新されることです。 (APIリクエストと検証に使用されるフォーム。)Vue.jsの計算されたプロパティを使用すると、追加の状態同期を必要とせずに、必要に応じて変換できます。