atuwebでは 「ITエンジニアのための技術書レビューサイト(以下、技術書サイト)」を開発、公開しております。
順番が前後しましたが、このサイトにようやくログイン機能追加を行いました。

ITエンジニアのための技術書レビューサイト(仮) 技術書Yome !!
https://book-yome.net/

Overview

LaravelとVue.jsで実装したサイトにTwitter連携を実装し、Twitterのプロフィールをユーザ情報として利用する

大雑把な処理の流れは次です。

  • フロントからTwitterの認証へ飛ばし、認証結果によってJWTトークンを発行
  • サーバからフロントエンドにトークンを引き渡し
  • フロントエンドからのリクエストにトークンを付加し、バックエンドで認証を行う

環境

  • PHP 7.0
  • Laravel 5.4
  • Vue.js 2.2.x
  • npm 4.5.x

Twitter連携

技術書サイトでは、thujohn/twitterを利用しております。
https://github.com/thujohn/twitter


処理フローは次の通りです。

  1. 技術書サイトよりTwitter側の認証画面にリダイレクト
  2. Twitter場でID/Passwordを入力すると指定コールバックURLに認証情報が渡る
  3. 認証結果のCredentialsからプロフィール情報を抽出

手順3でこのようなプロフィール情報が得られます。
技術書サイトでは、ここからニックネーム、スクリーン名、プロフィール画像URLをコピーしてサイト内のユーザー情報としてDB保存しています。

JWTとユーザ認証

JWTとはJSON Web Tokenの略です。

ざっくりいうと、認証済みのユーザを識別することができるURL Safeなトークンです。

「JSONが改ざんされていないか」 をチェックすることが可能で、SPAのトークンとして利用されることが増えているようです。


技術書サイトでは、tymon/jwt-authを利用しております。
https://github.com/tymondesigns/jwt-auth

JWTの生成

Twitterと関連づけたユーザ情報をSelectし、これを引数にトークンを生成します。

$token = JWTAuth::fromUser($user);

このトークンをレスポンスしてローカルストレージに保存します。

SPAからの認証ヘッダ

axiosを次のようにしAuthorizationヘッダにJWTトークンをセットします。

axios.interceptors.request.use(config => {
  config.headers['X-CSRF-TOKEN']     = window.Laravel.csrfToken
  config.headers['X-Requested-With'] = 'XMLHttpRequest'
  config.headers['Authorization']    = `Bearer ${localStorage.getItem('jwt-token')}`
  return config
})

サーバでのJWT認証

以下の1文でAuthorizationヘッダを読み込み、トークンのパースとユーザの認証までを行います。

$user = JWTAuth::parseToken()->authenticate();

トークンが無効であったり、有効期限切れの場合はJWTExceptionがスローされます。
実際にはtry-catchで処理してあげましょう。

ログイン状態の管理をStoreパターンで

ログイン状態など、複数のコンポーネントから参照するデータをどのようにアプリケーションに保持すれば良いでしょうか。

親コンポーネントから子コンポーネントへひたすらpropsでデータを渡すバケツリレー的実装が簡単に破綻してしまうことは目に見えています。


技術書サイトでは storeパターンを使って対応しました。

Vue.js > ガイド > 状態管理
https://jp.vuejs.org/v2/guide/state-management.html

Vue.jsやReactを勉強されている方なら目にしたことがあるでしょう、この図です。

(画像は 上記 「Vue.js > ガイド > 状態管理」 より引用)

このあたりの処理は、以前も参考にいたしました acro5pianoさんのqiita記事を、ほぼそのまま実装しております。

Laravel 5.4 と Vue.js 2.2 と JWTAuth で、ログインできる SPA アプリケーションのチュートリアル その4
http://qiita.com/acro5piano/items/eb29f13b82f386220460#_reference-3f516735e1f0890da252


userStoreとして下のようなモジュールを実装します。

// userStore.js
export default {
  state: {
    user: {},
    auth: false,
  },
  setCurrentUser() {
    http.get('self', res => {
      this.state.user = res.data.user
      this.state.auth = true
    })
  },
  init () {
    this.setCurrentUser()
  }
}

そして、アプリケーション生成時にuserStoreのinit()を呼び出し初期化を行います。

const app = new Vue({
  router,
  el: '#app',
  created () {
    userStore.init()
  },
})

各コンポーネントでは、この初期化済みのuserStoreを参照する、と言う流れです。


ガラケーの時代からWebアプリを作っているため「アプリケーションの生成時に余計にリクエストが飛んでしまう」と小さなことを気にしてしまいましたが、今はそんな時代ではないですね。。。

vue-routerでのページを切り替える際にuserStore.init()は呼び出されませんので、実際のコストは目をつぶることができるレベルです。

おわりに

全体のフローを駆け足で見てきました。
数年前のWebアプリと比較し、格段に進歩していますね。

技術書サイトは書籍の検索機能が貧弱なため、現在はタグ表示や書籍検索昨日の拡充を行なっています。
よろしければ以下ご覧ください

ITエンジニアのための技術書レビューサイト(仮) 技術書Yome !!
https://book-yome.net/




2017年07月09日:冒頭の文章に少し追記

スポンサーリンク
ad_336
ad_336
  • このエントリーをはてなブックマークに追加
  • Evernoteに保存Evernoteに保存