20200212のlaravelに関する記事は5件です。

【Laravel + Vue】 Laravel Airlock を使ってAPI認証を構築してみる

Laravel Airlockとは

Laravel Airlockは、SPAおよびシンプルなAPI向けの超軽量認証システムを提供します。

github
※初期バージョンですので利用は自己責任でお願いします

今回作業後の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.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>

emailpasswordv-modelloginFormにそれぞれ結びつけています。
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.js
register(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.js
const 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.js
logout(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がでて、いろんな方の情報が出てからでも良かった気がしてならない。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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公式)

以上です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【環境構築】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開けたらいけた!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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の機能群についても備忘録としてまとめてみようかなと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

WEBアプリ開発初心者が3つのWEBフレームワークを試してみる ~Laravelインストール編~

image.png

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/Laravel

Laravelはフルスタックの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月

image.png

ここでやること

  • Laravelのインストール
    • (事前準備) XAMPPのインストール (comporserで必要なPHPをインストールするため)
    • (事前準備) comporserのインストール
    • (事前準備) Laravelのインストール
  • 少し触ってみる
    • プロジェクト作成
    • Hello Worldページを作成
    • URLの階層にhelloworldを追加
    • プロジェクトをローカルPCにデプロイ
    • アクセスする

インストール

環境

  • windows 10

以下の公式ページを参考にしました。

https://laravel.com/

Installing Laravel

LaravelのインストールにはComporserを使用します。そのcomporserにはPHPのインストールが要件として必要です。したがって PHP(XAMPPにて)インストールComporserインストールLaravelという手順でLaravelを構築します

XAMPPをインストールする

https://www.apachefriends.org/index.html

Composerをインストールする

https://getcomposer.org/

途中で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 blog

Hello 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

image.png

所感

とりあえず構築はサクッとできました

LaravelはデフォルトでAWSのサービス(SQSやS3など)が利用できるようで、そのあたりも試せるような比較ができるとよいなと思っています。

またそもそも話として、WEB初学者なのでMVCやらなんやら覚えることが多そうです。

参考

https://proengineer.internous.co.jp/content/columnfeature/13814

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む