使用環境
Laravel 5.7
jwt-auth 1.0.0-rc3
Vue 2.5
vue-router 3.0
Laravel認証の設定
最初にLaravel側を作っていきましょう。
artisan
で標準のWeb認証機能をサクッと作ります。
ターミナルで下記コマンドを実行します。
$ php artisan make:auth $ php artisan migrate
シーダーでダミーのユーザー情報も登録しておきます。
database/seeds/UsersTableSeeder.php
<?php use Illuminate\Database\Seeder; class UsersTableSeeder extends Seeder { public function run() { DB::table('users')->insert([ [ 'name' => 'admin', 'email' => 'admin@example.com', 'password' => bcrypt('password'), 'remember_token' => null, 'created_at' => '2018-10-02 14:28:19', 'updated_at' => '2018-10-02 14:28:19' ] ]); } }
database/seeds/DatabaseSeeder.php
<?php use Illuminate\Database\Seeder; class DatabaseSeeder extends Seeder { public function run() { $this->call(UsersTableSeeder::class); } }
$ php artisan db:seed
これでデータベースにユーザー情報が登録され「admin@example.com」「password」の情報でログインできるようになります。
JWTAuthのインストールと設定
composerでインストールします。
Laravel 5.7 の場合はRC3を指定します。
$ composer require tymon/jwt-auth 1.0.0-rc3
次に設定ファイルを生成します。
下記コマンドを実行すると、config/jwt.php
が作成されます。
$ php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"
下記コマンドでシークレットキーを生成します。
$ php artisan jwt:secret
.env
にJWT_SECRET
が追記されているのを確認します。
モデル・コントローラーの修正
UserモデルにJWTSubjectインターフェイスを実装します。
メソッドはgetJWTIdentifier
とgetJWTCustomClaims
を作成します。
app/User.php
<?php namespace App; use Tymon\JWTAuth\Contracts\JWTSubject; use Illuminate\Notifications\Notifiable; use Illuminate\Foundation\Auth\User as Authenticatable; class User extends Authenticatable implements JWTSubject { use Notifiable; protected $fillable = [ 'name', 'email', 'password', ]; protected $hidden = [ 'password', 'remember_token', ]; public function getJWTIdentifier() { return $this->getKey(); } public function getJWTCustomClaims() { return []; } }
認証にJWTを使用するようにconfig/auth.php
を修正します。
config/auth.php
'defaults' => [ 'guard' => 'api', 'passwords' => 'users', ], // ... 'guards' => [ 'api' => [ 'driver' => 'jwt', 'provider' => 'users', ], ],
新しくAuthController
を作成します。
app/Http/Controllers/AuthController.php
<?php namespace App\Http\Controllers; class AuthController extends Controller { function login() { $credentials = request(['email', 'password']); if (!$token = auth('api')->attempt($credentials)) { return response()->json(['error' => 'Unauthorized'], 401); } return $this->respondWithToken($token); } public function logout() { auth()->logout(); return response()->json(['message' => 'ログアウトしました。']); } public function me() { return response()->json(auth()->user()); } protected function respondWithToken($token) { return response()->json([ 'access_token' => $token, 'token_type' => 'bearer', 'expires_in' => auth("api")->factory()->getTTL() * 60 ]); } }
今回はログイン・ログアウトとログイン情報を取得する機能だけ作成します。
次に、このコントローラーにアクセスするためのAPIルーターを設定します。
routes/api.php
Route::post('/login', 'AuthController@login'); Route::group(['middleware' => 'auth:api'], function () { Route::get('/me', 'AuthController@me'); Route::post('/logout', 'AuthController@logout'); });
login
はログインするためのAPIなので誰でもアクセスできます。
me
はログインした状態でしかアクセスできないようにmiddleware
に auth:api
を指定します。
ということでここまではほとんどjwt-authのドキュメントのままだったりします。
jwt-auth
ビューファイルの作成
今回はSPAなのでbladeファイルは一つだけ作成します。
resources/views/app.blade.php
<!doctype html> <html lang="ja"> <head> <meta charset="utf-8"> <title>LaravelSPA</title> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <link href="{{ mix('/css/app.css') }}" rel="stylesheet"> <meta name="csrf-token" content="{{ csrf_token() }}"> </head> <body> <div id="app"> <app></app> </div> <script src="{{ mix('/js/app.js') }}"></script> </body> </html>
webルーターは全てのアクセスでこのapp.blade.php
が読み込まれるように変更します。
routes/web.php
Route::any('{all}', function () { return view('app'); })->where(['all' => '.*']);
Vue Router のインストール
バックエンドはだいたいできましたということで、フロント(Vue)側を作成していきます。
Laravelはpackage.jsonが最初から設定されてますので、NodeモジュールをインストールするだけでVue.jsの開発環境が整います。
$ npm install
今回は VueRouter でフロント側のルーテイングしますのでインストールしましょう。
$ npm install vue-router
下記コマンドを実行してファイルを監視状態にして開発を進めます。
$ npm run watch
ストアの作成
ログイン状態を保持するためにストアファイルを作成します。
この部分はとりあえずな感じで簡易的なものにします。
resources/js/store.js
export default { state: { isLogin: false } }
app.js
を下記のようにします。
resources/js/app.js
require('./bootstrap'); import Vue from 'vue'; import store from './store'; import router from './router'; window.state = store.state; Vue.component('app', require('./components/App.vue')); const app = new Vue({ router }).$mount('#app');
window.state = store.state;
このようにすることで全てのファイルでstate
変数が使用できるようになります。
router
とApp.vue
の部分は次で作成します。
ルーターの作成
ルーターはログインが必要なページはmeta
にrequiresAuth: true
を指定します。
beforeEach
でログインしていないとlogin
ページにリダイレクトするように記述してます。
resources/js/router.js
import Vue from 'vue'; import VueRouter from 'vue-router'; import Home from './components/pages/Home' import Login from './components/pages/Login' import User from './components/pages/User' Vue.use(VueRouter); const routes = [ { path: '/', component: Home }, { path: '/login', component: Login }, { path: '/user', component: User, meta: { requiresAuth: true } } ]; const router = new VueRouter({ mode: 'history', routes }); router.beforeEach((to, from, next) => { if (to.matched.some(record => record.meta.requiresAuth)) { if (state.isLogin === false) { next({ path: '/login', query: { redirect: to.fullPath } }) } else { next() } } else { next(); } }); export default router;
App.vueコンポーネントの作成
最初にルーターの起点となるApp.vue
を作成します。
ログアウトは機能だけなのでここに書いてます。
resources/js/components/App.vue
<template> <div> <ul> <li><router-link to="/">ホーム</router-link></li> <li><router-link to="/login">ログイン</router-link></li> <li><router-link to="/user">ユーザー情報</router-link></li> <li @click="logout">ログアウト</li> </ul> <hr> <router-view></router-view> </div> </template> <script> export default { methods: { logout() { axios.post('/api/logout').then(res => { axios.defaults.headers.common['Authorization'] = ''; state.isLogin = false; this.$router.push({path: '/'}); }); } } } </script>
次からはルーターで設定した、各コンポーネントファイルを作成していきます。
Login.vueコンポーネントの作成
ログインページ用のコンポーネントです。
resources/js/components/pages/Login.vue
<template> <div> <p v-show="isError">認証に失敗しました。</p> <form @submit.prevent="login"> <h1>ログイン</h1> メールアドレス: <input type="email" v-model="email"> パスワード: <input type="password" v-model="password"> <button type="submit" class="btn btn-primary">ログイン</button> </form> </div> </template> <script> export default { data () { return { isError: false, email: '', password: '', } }, methods: { login() { axios.post('/api/login', { email: this.email, password: this.password }).then(res => { const token = res.data.access_token; axios.defaults.headers.common['Authorization'] = 'Bearer ' + token; state.isLogin = true; this.$router.push({path: '/'}); }).catch(error => { this.isError = true; }); } } } </script>
ログインに成功したら、axios
のAuthorization
にトークンを入れます。
これでログインが必要なAPIにアクセスができるようになります。
User.vueコンポーネントの作成
ログインした後ユーザー情報を取得して表示するためのコンポーネントです。
resources/js/components/pages/User.vue
<template> <div> <p v-show="isError">情報の取得に失敗しました。</p> <h1>ユーザー情報</h1> <table> <tr> <th>ID</th> <td>{{ user.id }}</td> </tr> <tr> <th>ユーザー名</th> <td>{{ user.name }}</td> </tr> <tr> <th>メール</th> <td>{{ user.email }}</td> </tr> <tr> <th>登録日</th> <td>{{ user.created_at }}</td> </tr> </table> </div> </template> <script> export default { data () { return { isError: false, user: {} } }, created() { axios.get('/api/me').then(res => { this.user = res.data; }).catch(error => { this.isError = true; }); } } </script>
ざっくりではありますが、なんとなく動きがわかったかなということで以上にになります。