- 投稿日:2020-02-12T18:46:42+09:00
【Laravel + Vue】 Laravel Airlock を使ってAPI認証を構築してみる
Laravel Airlockとは
Laravel Airlockは、SPAおよびシンプルなAPI向けの超軽量認証システムを提供します。
? I'd like to get some beta testers on Laravel Airlock... I have put up initial documentation here: https://t.co/BFPasv1BCZ
— Taylor Otwell ? (@taylorotwell) January 8, 2020github
※初期バージョンですので利用は自己責任でお願いします今回作業後のGithubに上げたサンプル
Laravel
前提
Laravel 6.x インストール済み
airlockインストール
composer require laravel/airlock構成ファイルの公開
php artisan vendor:publish --provider="Laravel\Airlock\AirlockServiceProvider"マイグレーション実行
php artisam migrate
CreatePersonalAccessTokensTable
が実行され、personal_access_tokens
テーブルが追加されます。
また、config/airlock.php
も追加されますミドルウェアの追加
app/Http/Kernel.php
に
use Laravel\Airlock\Http\Middleware\EnsureFrontendRequestsAreStateful;
EnsureFrontendRequestsAreStateful::class,
を追加しますapp/Http/Kernel.php<?php namespace App\Http; use Illuminate\Foundation\Http\Kernel as HttpKernel; // ↓追加 use Laravel\Airlock\Http\Middleware\EnsureFrontendRequestsAreStateful; class Kernel extends HttpKernel { /** * The application's global HTTP middleware stack. * * These middleware are run during every request to your application. * * @var array */ protected $middleware = [ \App\Http\Middleware\TrustProxies::class, \App\Http\Middleware\CheckForMaintenanceMode::class, \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class, \App\Http\Middleware\TrimStrings::class, \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class, ]; /** * The application's route middleware groups. * * @var array */ protected $middlewareGroups = [ 'web' => [ \App\Http\Middleware\EncryptCookies::class, \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, \Illuminate\Session\Middleware\StartSession::class, // \Illuminate\Session\Middleware\AuthenticateSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, \App\Http\Middleware\VerifyCsrfToken::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, ], 'api' => [ EnsureFrontendRequestsAreStateful::class, //追加 'throttle:60,1', 'bindings', ], ]; ~略~ }APIトークンの発行
App\User
に
use Laravel\Airlock\HasApiTokens;
use HasApiTokens
を追加しますApp\User<?php namespace App; use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; use Laravel\Airlock\HasApiTokens; //追加 class User extends Authenticatable { // ↓追加 use HasApiTokens, Notifiable; /** * The attributes that are mass assignable. * * @var array */ protected $fillable = [ 'name', 'email', 'password', ]; /** * The attributes that should be hidden for arrays. * * @var array */ protected $hidden = [ 'password', 'remember_token', ]; /** * The attributes that should be cast to native types. * * @var array */ protected $casts = [ 'email_verified_at' => 'datetime', ]; }ルーティング
routes/api.php
を下記のようにしますroutes/api.php<?php use Illuminate\Http\Request; Route::group(['middleware' => ['auth:airlock']], function () { Route::get('user', function (Request $request) { return response()->json(['user' => $request->user()]); }); Route::post('logout', 'Auth\Api\LoginController@logout')->name('api.logout'); }); Route::post('register', 'Auth\Api\RegisterController@register')->name('api.register'); Route::post('login', 'Auth\Api\LoginController@login')->name('api.login');ユーザー登録
app/Http/Controllers/Auth
内にApi
フォルダを作成し、そこにapp/Http/Controllers/Auth
内のRegisterController
をコピーし、下記を貼り付けますApi/RegisterController.php<?php namespace App\Http\Controllers\Auth\Api; use App\Http\Controllers\Controller; use App\User; use Illuminate\Foundation\Auth\RegistersUsers; use Illuminate\Http\Request; use Illuminate\Support\Facades\{Validator, Hash, DB}; class RegisterController extends Controller { use RegistersUsers; public function register(Request $request) { // リクエストを検証します。 $this->validator($request->all())->validate(); // ユーザーの作成とトークンの作成します $data = DB::transaction(function () use ($request) { $user = $this->create($request->all()); $token = $user->createToken($request->device_name ?? 'undefined')->plainTextToken; return json_encode(['token' => $token, 'user' => $user]); }); // userとtokenのjsonを返却 return response($data); } /** * Get a validator for an incoming registration request. * * @param array $data * @return \Illuminate\Contracts\Validation\Validator */ protected function validator(array $data) { return Validator::make($data, [ 'name' => ['required', 'string', 'max:255'], 'email' => ['required', 'string', 'email', 'max:255', 'unique:users'], 'password' => ['required', 'string', 'min:8', 'confirmed'], ]); } /** * Create a new user instance after a valid registration. * * @param array $data * @return \App\User */ protected function create(array $data) { return User::create([ 'name' => $data['name'], 'email' => $data['email'], 'password' => Hash::make($data['password']), ]); } }ログイン・ログアウト処理
登録のときと同様に、
Auth
内のLoginController
をコピーし
下記のように修正します。Api\LoginController<?php namespace App\Http\Controllers\Auth\Api; use App\Http\Controllers\Controller; use App\User; use Illuminate\Foundation\Auth\AuthenticatesUsers; use Illuminate\Http\Request; use Illuminate\Support\Facades\Hash; use Illuminate\Validation\ValidationException; class LoginController extends Controller { use AuthenticatesUsers; /** * Handle a login request to the application. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\Response|\Illuminate\Http\JsonResponse * * @throws \Illuminate\Validation\ValidationException */ public function login(Request $request) { // バリデーション $this->validateLogin($request); // ユーザーの取得 $user = User::where('email', $request->email)->first(); // 取得できない場合、パスワードが不一致の場合エラー if (!$user || !Hash::check($request->password, $user->password)) { throw ValidationException::withMessages([ 'email' => [__('failed')], ]); } // tokenの作成 $token = $user->createToken($request->device_name ?? 'undefined')->plainTextToken; return response()->json(['token' => $token, 'user' => $user], 200); } /** * Handle a logout request to the application. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\Response|\Illuminate\Http\JsonResponse */ public function logout(Request $request) { $user = $request->user(); // tokenの削除 foreach ($user->tokens as $token) { $token->delete(); } return response()->json(['message' => 'logouted']); } }これで登録・ログイン・ログアウトはできるようになりました。
Airlock テスト pic.twitter.com/UI75JJX9sq
— 棗(なつめ) (@natsume_aurlia) February 11, 2020※別ドメインの場合、
airlock.php
ファイルのstateful構成オプションを使用してくださいVue
前提
Vue、Vuex、VueRouterインストール済みのプロジェクト作成済み
Laravelに同居する形でもできますが、今回は分離させます。
みやすさ重視、改造しやすいようにスタイルは当ててませんこちらのコードを参考にさせていただきました
各エンドポイントへリクエストを送信し、認証後にトークンを受け取り
認証が必要なアクションにはそのトークンがを付与したリクエストを行います。ログイン画面
/src/views/Login.vue<template> <div> <h1>login</h1> <form @submit.prevent="login"> <label for="email">Email</label> <input type="text" id="email" v-model="loginForm.email" /> <label for="password">Password</label> <input type="password" id="password" v-model="loginForm.password" /> <button type="submit">login</button> </form> </div> </template> <script> export default { name: "login", components: {}, data() { return { loginForm: { email: "", password: "" } }; }, methods: { login() { this.$store.dispatch("auth/login", this.loginForm).then(() => { this.$router.push({ name: "dashboard" }); }); } } }; </script>
password
をv-model
でloginForm
にそれぞれ結びつけています。
submit
されたとき、画面遷移しないようにしlogin
のメソッドを呼び出します登録画面
/src/views/Register.vue<template> <div> <h1>Register</h1> <form @submit.prevent="register"> <label for="name">Name</label> <input type="text" id="name" v-model="registerForm.name" /> <label for="email">E-mail Address</label> <input type="email" id="email" v-model="registerForm.email" /> <label for="password">Password</label> <input type="password" id="password" v-model="registerForm.password" /> <label for="password_confirm">Confirm Password</label> <input type="password" id="password_confirm" v-model="registerForm.password_confirmation" /> <button type="submit">register</button> </form> </div> </template> <script> export default { name: "register", components: {}, data() { return { registerForm: { name: "", email: "", password: "", password_confirmation: "" } }; }, methods: { register() { this.$store.dispatch("auth/register", this.registerForm).then(() => { this.$router.push({ name: "dashboard" }); }); } } }; </script>ここも普通ですね
password_confirmation
なのはLaravel側でconfirmed
というバリデーションルールが有るためですフィールドがそのフィールド名+
_confirmation
フィールドと同じ値であることをバリデートします。
例えば、バリデーションするフィールドがpassword
であれば、
同じ値のpassword_confirmation
フィールドが入力に存在していなければなりません。ストア側
登録・ログインは下記のようにリクエストをするだけです。
auth.jsregister(context, data) { axios.post(BASE_URL + '/api/register', data).then((result) => { context.commit("setUser", result.data.user); context.commit("setToken", result.data.token); }).catch(error => { console.log(`Error! HTTP Status: ${error}`); }); },公式に下記のようにリクエストして、CSRF保護を初期化してからリクエストするようにありますが、省いてます。
axios.defaults.withCredentials = true; axios.get('/airlock/csrf-cookie').then(response => { // Login... });ログインや登録で取得したトークンはローカルストレージへ保存し
state
ではローカルストレージを参照するようにしますauth.jsconst state = { user: null, token: window.localStorage.getItem('token') } const mutations = { setUser(state, user) { state.user = user; }, setToken(state, token) { window.localStorage.setItem('token', token); } }ログアウト、ユーザ情報取得
保存してあるトークンを付与して、ログアウトのエンドポイントへリクエストを送信するだけです。
postやgetの第三引数に指定します。
もっと簡潔に出来るパッケージや手法もあるようです。auth.jslogout(context) { axios.post(BASE_URL + '/api/logout', null, { headers: { Authorization: `Bearer ${state.token}`, } }).then((result) => { context.commit("setUser", null); context.commit("setToken", null); }).catch(error => { console.log(`Error! HTTP Status: ${error}`); }); },参考記事
https://github.com/laravel/airlock
https://redfern.dev/vue-js-auth-using-laravel-airlock/
https://github.com/garethredfern/airlock-vue
日本語の情報が0で、英語の情報もほぼ公式しかなかったのできつかったです。
Laravel7がでて、いろんな方の情報が出てからでも良かった気がしてならない。
- 投稿日:2020-02-12T13:41:02+09:00
Laravel root/storage/vender/public/ までのpath取得方法
Laravelで簡単にstorageやvendorフォルダまでのパスを取得する方法について明記します。
公式ドキュメントそのままですが、
Paths
app_path()
Get the fully qualified path to the app directory.
(Laravelアプリケーション内の appディレクトリへのパスを取得できます。)
使用例$path = app_path();base_path()
Get the fully qualified path to the root of the application install.
(Laravelアプリケーション内のルートパスを取得できます。)
public_path()
Get the fully qualified path to the public directory.
(Laravelアプリケーション内の publicディレクトリまでのパスを取得できます。)
storage_path()
Get the fully qualified path to the storage directory.
(Laravelアプリケーション内の storageディレクトリまでのパスを取得できます。)
参考ページ
Helper Functions - Laravel - The PHP Framework For Web Artisans #Path (Laravel公式)以上です。
- 投稿日:2020-02-12T12:20:02+09:00
【環境構築】vagrant upでエラー
vagrant up
でエラーターミナル$ vagrant up /opt/vagrant/embedded/gems/2.2.7/gems/vagrant-2.2.7/lib/vagrant/util/which.rb:37: warning: Insecure world writable dir /usr/local/opt in PATH, mode 040777 Bringing machine 'homestead' up with 'virtualbox' provider... ==> homestead: Importing base box 'laravel/homestead'... Progress: 90%There was an error while executing `VBoxManage`, a CLI used by Vagrant for controlling VirtualBox. The command and stderr is shown below. Command: ["import", "/Users/namari/.vagrant.d/boxes/laravel-VAGRANTSLASH-homestead/9.2.0/virtualbox/box.ovf", "--vsys", "0", "--vmname", "ubuntu-18.04-amd64_1581476571710_32924", "--vsys", "0", "--unit", "11", "--disk", "/Users/namari/VirtualBox VMs/ubuntu-18.04-amd64_1581476571710_32924/ubuntu-18.04-amd64-disk001.vmdk"] Stderr: 0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100% Interpreting /Users/namari/.vagrant.d/boxes/laravel-VAGRANTSLASH-homestead/9.2.0/virtualbox/box.ovf... OK. 0%... Progress state: NS_ERROR_INVALID_ARG VBoxManage: error: Appliance import failed VBoxManage: error: Code NS_ERROR_INVALID_ARG (0x80070057) - Invalid argument value (extended info not available) VBoxManage: error: Context: "RTEXITCODE handleImportAppliance(HandlerArg *)" at line 1118 of file VBoxManageAppliance.cpp原因
容量不足だった・・・・・
1G開けたらいけた!
- 投稿日:2020-02-12T01:02:47+09:00
inputタグの値を配列形式で扱った時の備忘録(with Blade of Laravel)
配列の値をControllerで受け取るってなんか難しくないか(?)
と始める前に思っていて、実際割とハマったので書き残しておこうかなと思います。
配列形式でpostする書き方
exaple.html<input name="test[]" type="text"> //result //test[0] <input name="test[0][1]['test']" type="text"> //result //test=[ 0 =>[ 0 => [], 1 => [ 'test' => '', ] ] ]少し落ち着いてきたら、最近使ったLaravelの機能群についても備忘録としてまとめてみようかなと思います。
- 投稿日:2020-02-12T00:01:40+09:00
WEBアプリ開発初心者が3つのWEBフレームワークを試してみる ~Laravelインストール編~
Why
これまでWEBアプリケーションの開発はやらずにBIツールを利用しようと思っていたのですが、
BIツールではより細かい調整やデータの入力などが実現できないことがあった
バックエンドをメインでやっていきたいが、WEBアプリケーションがどのように動いているのか泥臭いところを知りたい
といったことがあり、実際に触ってみようと思いました。
そこで開発言語とフレームワークについて調べたところ、
PHP
+Laravel
,Ruby
+Rails
,Python
+Django
では初心者目線ではそれぞれに大差はないように感じましたので、簡単なアプリを作って試そうという内容です。その第一回として
PHP
+Laravel
についてインストールまでやってみました。Laravelって?
PHPにて書かれたオープンソース(MITライセンス)のWebアプリケーションフレームワークのようです。
PHPは結構自由に書ける言語のようですが、Laravelなどのフレームワークを使うことでコードの管理がしやすくなるといったメリットがあるようです。
特徴は?
wikipediaを見ると以下のようなことが書かれてました
https://ja.wikipedia.org/wiki/LaravelLaravelはフルスタックのWebアプリケーションフレームワークであり、ルーティング、リエクスト処理、ビュー、クエリビルダー、ORM(オブジェクト関係マッピング)、DI(依存性の注入)、認証、ユニットテスト、ブラウザテスト等、現代的なフレームワークが要求される一通りの機能を実装している。またコンソールからArtisanコマンド(Ruby on Railsのrailsコマンドに相当)を利用することにより、コントローラやビューの雛形の作成、データベーススキーマの作成等、一通りの作業を行うことができる。また、ユーザのオリジナルのArtisanコマンドを作成したり、コントローラ等の通常のコード内からArtisanコマンドを呼び出すといったこともできる。
ほかのPHPフレームワークとの違いは?
ほかにもPHPフレームワークはあるようですが、Google Trendsを見てもLaravelが圧倒しているようです。
技術 初版 CakePHP 2005年4月 CodeIgniter 2006年2月 Symfony 2007年1月 Laravel 2011年6月 FuelPHP 2011年8月 ここでやること
- Laravelのインストール
- (事前準備) XAMPPのインストール (comporserで必要なPHPをインストールするため)
- (事前準備) comporserのインストール
- (事前準備) Laravelのインストール
- 少し触ってみる
- プロジェクト作成
- Hello Worldページを作成
- URLの階層に
helloworld
を追加- プロジェクトをローカルPCにデプロイ
- アクセスする
インストール
環境
- windows 10
以下の公式ページを参考にしました。
Installing Laravel
LaravelのインストールにはComporserを使用します。そのcomporserにはPHPのインストールが要件として必要です。したがって
PHP(XAMPPにて)インストール
→Comporserインストール
→Laravel
という手順でLaravelを構築しますXAMPPをインストールする
https://www.apachefriends.org/index.html
Composerをインストールする
途中でPHPのパスを求められますので、
<YOUR_XAMPP_DIR>/php/php.exe
(デフォルトはC:\xampp\php\php.exe
)入力します。Laravelのインストール
https://laravel.com/docs/6.x#installation
Via Laravel Installer
以下のコマンドを実行
composer global require laravel/installer少し触ってみる
プロジェクトの作成
laravel new blogHello Worldページを作成
resources/views/welcome.blade.php
をコピーし、resources/views/helloworld.blade.php
となるように保存するその後内容を変更する
内容は単純で、
Hello World
と表示するようにしたもの・・・ <body> <div class="flex-center position-ref full-height"> <div class="content"> <div class="title m-b-md"> Hello World </div> </div> </div> </body> ・・・URLの階層に
helloworld
を追加
routes/web.php
のファイル内の末尾に、以下の記述を追加するRoute::get('helloworld', function () { return view('helloworld'); });プロジェクトのデプロイ
以下のコマンドを実行し、localhostに8000ポートでホストさせる
php artisan serve --host localhost --port 8000アクセスする
以下のURLにアクセスすると、画面が表示される
http://localhost:8000/helloworld
所感
とりあえず構築はサクッとできました
LaravelはデフォルトでAWSのサービス(SQSやS3など)が利用できるようで、そのあたりも試せるような比較ができるとよいなと思っています。
またそもそも話として、WEB初学者なのでMVCやらなんやら覚えることが多そうです。
参考
https://proengineer.internous.co.jp/content/columnfeature/13814