SPAを作成する場合のLaravel + vue.js + JWTを使い、emailを送信し、仮登録を行い、本登録をする流れを作成します。
これらを使って認証を行なっているものが少なかったのでまとめておきます。
環境は以下の通りです。
Node | npm | Vue.js | Vue Router | Vuex | PHP | Laravel |
---|---|---|---|---|---|---|
12.4.0 | 6.9.0 | 2.5.17 | 3.1.2 | 3.1.1 | 7.1.16 | 5.8.34 |
長かったので2パートに分けてあります。前半はjwtで認証を行うまでの設定を行い、後半でemailを使った仮登録から本登録までの流れを実装します。
laravelに慣れていない場合は、LaravelのドキュメントまたはLaravelに関する他の記事を参照して、あらかじめ理解を深めてください。
メールの認証にはメールテストツールのMailHogを使用します。
今回はlaravelで用意されているmake:authは使用しません。
早速やっていきましょう。
JWT(JSON Web Token)とは
わかる方は飛ばしてください。
属性情報 (Claim) をJSONデータ構造で表現したトークンの仕様のことで、認証なんかに使われます。
jwtは以下のもので構成されており、これら3つのパーツが . で区切られた文字列のことを指します
- ヘッダー(Base64文字列)
- クレーム情報、つまりデータ本体(Base64文字列)
- 署名(バイナリ文字列)
ヘッダー
メタ情報を含めます。alg には署名に使われているアルゴリズムを指定します。これは必須です。
typ はトークンタイプを表します。”JWT”が推奨されますが、これは任意です。
クレームデータ
JSONをBase64urlエンコードした文字列
クレーム | 説明 |
---|---|
iss | JWT発行者 |
sub | JWT発行者から払い出されたユーザ識別子。文字列またはURIで表わされる。⇒社員番号を設定 |
aud | JWTを利用することが想定された主体の識別子一覧 |
exp | JWTの有効期限を示す。⇒有効期限を設定 |
nbf | JWTが有効になる日時を示す |
iat | JWTを発行した時刻を示す |
jti | JWTのための一意な識別子。JWTのリプレイを防ぐことに利用する |
typ | typヘッダパラメータと同じ値空間および同じ規則が適用される |
署名
主に署名時に使用され、データが改ざんされているかどうかをチェックするために使われます。
今回は、jwt-authを利用してjwtの認証を行います。
Laravel アプリケーションのセットアップ
ここは好きな名前に変えてください。今回は、laravel_jwt_vuejs_emailという名前でプロジェクトを作成します。
composer create-project --prefer-dist laravel/laravel laravel_jwt_vuejs_email
cd laravel_jwt_vuejs_email
JWT認証パッケージを追加
composer require tymon/jwt-auth:dev-develop
「dev-develop」はまだ開発中であることを意味しています。
JWT設定の公開
artisan コマンドを使用してJWTパッケージ構成を公開します。
php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"
config/app.phpに以下を追加してください。
app.php
'providers' => [
Tymon\JWTAuth\Providers\LaravelServiceProvider::class,
]
'aliases' => [
'JWTAuth' => Tymon\JWTAuth\Facades\JWTAuth::class,
],
JWTシークレットを作成する
秘密鍵を生成します。
php artisan jwt:secret
認証設定の更新
config/auth.phpのデフォルトのガードとAPIドライバーを以下のように更新します
'defaults' => [ 'guard' => 'api', 'passwords' => 'users', ], 'guards' => [ 'web' => [ 'driver' => 'session', 'provider' => 'users', ], 'api' => [ 'driver' => 'jwt', 'provider' => 'users', ], ],
'providers' => [ 'users' => [ 'driver' => 'eloquent', 'model' => App\User::class, ], ],
ユーザーモデルの更新
app/User.phpを以下のように変更します。
<?php
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Tymon\JWTAuth\Contracts\JWTSubject;
class User extends Authenticatable implements JWTSubject
{
use Notifiable;
protected $fillable = [
'name', 'email', 'password', 'role'
];
protected $hidden = [
'password', 'remember_token',
];
public function getJWTIdentifier()
{
return $this->getKey();
}
public function getJWTCustomClaims()
{
return [];
}
}
getJWTIdentifierはjwtのトークンを取得するための関数です。
getJWTCustomClaimsは、前述したjwtのクレームデータの情報以外に追加的にjwtトークンに情報を追加したい時、この関数に戻り値(Return value)を修正します。
認証ミドルウェアの更新
app/Http/Middleware/Authenticate.php を更新します。handle と authenticate をオーバーライドし、json形式で返しています。この設定ではJSON形式の応答が必要なログインページのVue Jsで使用されるのでjson形式で返しています。
Authenticate.php
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Auth\Middleware\Authenticate as Middleware;
class Authenticate extends Middleware
{
// Override handle method
public function handle($request, Closure $next, ...$guards)
{
if ($this->authenticate($request, $guards) === 'authentication_failed') {
return response()->json(['error'=>'Unauthorized'],400);
}
return $next($request);
}
// Override authentication method
protected function authenticate($request, array $guards)
{
if (empty($guards)) {
$guards = [null];
}
foreach ($guards as $guard) {
if ($this->auth->guard($guard)->check()) {
return $this->auth->shouldUse($guard);
}
}
return 'authentication_failed';
}
}
ルートの設定
routes/api.php に以下ルートを追加してください。
api.php
Route::prefix('auth')->group(function () {
Route::post('register', 'AuthController@register');
Route::post('login', 'AuthController@login');
// Refresh the JWT Token
Route::get('refresh', 'AuthController@refresh');
Route::middleware('auth:api')->group(function () {
// Get user info
Route::get('user', 'AuthController@user');
// Logout user from application
Route::post('logout', 'AuthController@logout');
});
});
認証用コントローラーの作成
php artisan make:controller AuthController
app/Http/AuthController.php が作成されるので、以下のように変更してください。
AuthController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Auth;
use JWTAuth;
use Validator;
use App\User;
class AuthController extends Controller
{
/**
* Register a new user
*/
public function register(Request $request)
{
$v = Validator::make($request->all(), [
'name' => 'required|min:3',
'email' => 'required|email|unique:users',
'password' => 'required|min:3|confirmed',
]);
if ($v->fails())
{
return response()->json([
'status' => 'error',
'errors' => $v->errors()
], 422);
}
$user = new User();
$user->name = $request->name;
$user->email = $request->email;
$user->password = bcrypt($request->password);
$user->save();
return response()->json(['status' => 'success'], 200);
}
/**
* Login user and return a token
*/
public function login(Request $request)
{
$credentials = $request->only('email', 'password');
if ($token = $this->guard()->attempt($credentials)) {
return response()->json(['status' => 'success'], 200)->header('Authorization', $token);
}
return response()->json(['error' => 'login_error'], 401);
}
/**
* Logout User
*/
public function logout()
{
$this->guard()->logout();
return response()->json([
'status' => 'success',
'msg' => 'Logged out Successfully.'
], 200);
}
/**
* Get authenticated user
*/
public function user(Request $request)
{
$user = JWTAuth::parseToken()->authenticate();
return response()->json([
'status' => 'success',
'data' => $user
]);
}
/**
* Refresh JWT token
*/
public function refresh()
{
if ($token = JWTAuth::getToken()) {
return response()
->json(['status' => 'successs'], 200)
->header('Authorization', $token);
}
return response()->json(['error' => 'refresh_token_error'], 401);
}
/**
* Return auth guard
*/
private function guard()
{
return Auth::guard();
}
}
register()
バリデーションを行い、検証後に新しいユーザーを作成します。
login()
Auth :: guard()を使用しています。 attempt() メソッドは指定された資格情報をチェックし、成功後、応答のヘッダーで返されるトークンを生成します。
refresh()
現在のトークンの有効期限が切れている場合、refresh() はトークンを再生成します。config / jwt.php でトークンの期間を指定できます。
jwt.php
'ttl' => env('JWT_TTL', 60),
このように jwt.php で指定されており、JWT_TTLは、.env ファイルのJWT_TTLで設定します。
.env
JWT_TTL=10
デフォルトでは、JWTトークンは60分間(1時間)有効です。
logout()
トークンをリセットします。
user()
ユーザー情報を取得します。
ユーザーの作成
php artisan migrate
Vuexのインストール
npm install vuex --save
Vue Routerのインストール
npm install vue-router
依存関係をインストールしてください。
npm install
ここまででpackage.jsonは以下のようになっています。
package.json
{
"private": true,
"scripts": {
"dev": "npm run development",
"development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
"watch": "npm run development -- --watch",
"watch-poll": "npm run watch -- --watch-poll",
"hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js",
"prod": "npm run production",
"production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
},
"devDependencies": {
"axios": "^0.18.1",
"bootstrap": "^4.1.0",
"browser-sync": "^2.26.3",
"browser-sync-webpack-plugin": "2.0.1",
"cross-env": "^5.1",
"laravel-mix": "^4.0.7",
"popper.js": "^1.12",
"resolve-url-loader": "^2.3.1",
"sass": "^1.15.2",
"sass-loader": "^7.1.0",
"vue": "^2.5.17",
"vue-template-compiler": "^2.6.10"
},
"dependencies": {
"vue-router": "^3.1.3",
"vuex": "^3.1.1"
}
}
次回は、vueファイルを作成し、emailで仮登録から本登録を行うまでをやっていきます。