LaravelのEloquentでGroupByの使い方について、まとめたいと思います。 まずはデータアクセス方法を整理 Laravelはデータソースに対するデータアクセス方法として、Eloquent…
フロントをVue.js、バックエンドをLaravel(API)として構成する場合のLaravel/Sanctumの構成方法です。
Laravel/Sanctumとは
公式で推奨されているSPA向けの認証パッケージですが、如何せんLaravelの中の人はJetStream
押しが強く詳細なリファレンスが用意されていません。
JetStreamを使えばパッとスキャフォールディングで出来ますよ、とPRするのは良いけれど漏れなくinertia
が付いてきたり、知りたいのはVuexやrouter、axios
で実装する方法です。
プロジェクト用ルートディレクトリを作成する
Laravel内でVue.jsを使用せず、Server, Clientで分けて極力依存しないように構成にしたいと思います。
また、Docker等も理解していれば便利なので使いたくなりますが、今回は本題ではないので余計なものは使わず進めます。
mkdir sanctum_example
Laravelをインストールする
Laravelをcurlからインストールします。
URLのserver
の部分がディレクトリ名になるので任意で変更します。
cd sanctum_example
curl -s https://laravel.build/server | bash
Vueをインストールする
Vue CLIをインストールする
VueをコマンドラインからインストールするためにCLIをインストールします。
Node.jsをインストールしていなければ事前にインストールしておきます。
npm install -g @vue/cli
Vue.jsをインストール
対話式で構成を決めていきます。
認証状態をアプリケーション全体で保持するために、vuex, vue routerを選択してインストールします。
vue create client
Laravel/Sanctumをインストール
serverディレクトリに移動してからcomposerからLaravel/Sanctumをインストールします。
composerをインストールしていなければ事前にインストールしておきます。
composer require laravel/sanctum
設定ファイルとマイグレーションファイルを追加
sanctum設定ファイルを追加し、APIトークンを保存するフィールドを作成するマイグレーションファイルを追加します。
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
マイグレーションを実行します。
php artisan migrate
SPA認証 クッキーベースの認証
Sanctumを使ったSPAの認証にはトークンは使用せず、クッキーベースの認証が推奨されています。
Sanctumミドルウェアを追加する
server/app/Http/Kernel.php
'api' => [
\Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
'throttle:api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
CORSを設定する
異なるドメイン間で使用する場合は、supports_credentials
をtrue
に設定します。
server/config/cors.php
'paths' => ['api/*', 'sanctum/csrf-cookie'],
'allowed_methods' => ['*'],
'allowed_origins' => ['*'],
'allowed_origins_patterns' => [],
'allowed_headers' => ['*'],
'exposed_headers' => [],
'max_age' => 0,
'supports_credentials' => true,
Clientのドメイン・ポートを追加する
CORSによってアクセスされるクライアントのドメインを、Laravel側の.envで設定します。
SANCTUM_STATEFUL_DOMAINS=localhost:8080
SESSION_DRIVERをcookieへ変更する
同じく.envのSESSION_DRIVERを変更します。
SESSION_DRIVER=cookie
LoginControllerを追加する
スキャフォールディングは用意されていないため、Laravelの標準の認証に従って認証機能を実装していきます。
server/app/Http/Controllers/Auth/LoginController.php
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Validation\ValidationException;
class LoginController extends Controller
{
public function login(Request $request)
{
$credentials = $request->validate([
'email' => 'required|email',
'password' => 'required'
]);
if (Auth::attempt($credentials)) {
return response()->json(['message' => 'Login successful'], 200);
}
return response()->json(['message' => 'User not found'], 422);
}
public function logout()
{
Auth::logout();
return response()->json(['message' => 'Logged out'], 200);
}
}
ルーティングを追加する
ログイン・ログアウトのルーティングを追加します。
server/routes/api.php
Route::prefix('auth')->group(function () {
Route::post('/login', [App\Http\Controllers\Auth\LoginController::class, 'login']);
Route::post('/logout', [App\Http\Controllers\Auth\LoginController::class, 'logout']);
});
クライアント側(Vue.js)のログイン機能を追加する
storeを定義
vuexで認証状態・認証ユーザー情報を保持するようにします。
client/src/store/auth.js
import axios from 'axios';
export default {
namespaced: true,
state: {
isAuth: false,
user: null,
},
getters: {
isAuth(state) {
return state.isAuth;
},
user(state) {
return state.user;
},
},
mutations: {
SET_IS_AUTH(state, value) {
state.isAuth = value;
},
SET_USER(state, value) {
state.user = value;
},
},
actions: {
async login({ dispatch }, credentials) {
await axios.get('/sanctum/csrf-cookie');
await axios.post('/api/auth/login', credentials);
return await dispatch('me');
},
async me({ commit }) {
return await axios
.get('/api/user')
.then(response => {
commit('SET_IS_AUTH', true);
commit('SET_USER', response.data);
})
.catch(() => {
commit('SET_IS_AUTH', false);
commit('SET_USER', null);
});
},
},
};
Loginページを作成する
ログインページを作成します。
client/src/views/Login.vue
<template>
<div>
<form @submit.prevent="submit">
<div>
<label>Eメール</label>
<input type="text" v-model="form.email" />
</div>
<div>
<label>パスワード</label>
<input type="password" v-model="form.password" />
</div>
<button type="submit">ログイン</button>
</form>
</div>
</template>
<script>
import { mapActions } from 'vuex';
export default {
data() {
return {
form: {
email: '',
password: '',
},
};
},
methods: {
...mapActions({
login: 'auth/login',
}),
async submit() {
await this.login(this.form);
this.$router.replace({ name: 'Home' });
},
},
};
</script>
ルーティングを定義
/login
にリクエストを受けたら、Loginページへ遷移するように定義します。
Homeページは認証されていなければLoginページへリダイレクトするようにbeforeEach
で設定します。
client/src/router/index.js
import Vue from 'vue';
import VueRouter from 'vue-router';
import Home from '../views/Home.vue';
import Login from '@/views/Login.vue';
import Store from '@/store/index.js';
Vue.use(VueRouter);
const routes = [
{
path: '/login',
name: 'Login',
component: Login,
},
{
path: '/',
name: 'Home',
component: Home,
meta: {
isAuthenticated: true,
},
},
];
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes,
});
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.isAuthenticated)) {
if (!Store.state.auth.isAuth) {
next({ name: 'Login' });
} else {
next();
}
}
next();
});
export default router;
認証テスト用ユーザーを作成する
tinkerを使って直接コマンドラインから追加します。
php artisan tinker
\App\Models\User::factory()->create(['name' => 'genba neko', 'email' => 'neko@neko.jp', 'password' => bcrypt('neko')]);
確認
npm run serve
からVueを実行して/login
ページを表示します。
認証テスト用ユーザーの認証情報を入力して、認証に成功するか確認しましょう。
コメントを書く