Pendahuluan
Laravel memulai debutnya dengan menghadirkan fitur yang sangat praktis dalam meng-handle proses authentication. Hal ini menyebabkan penggunanya mendapatkan sedikit kenyamanan karena tidak perlu lagi secara repot memikirkan dan mengerjakan authentication.
Belakangan, muncul sebuah masalah, karena terlalu nyaman dengan hal praktis, banyak pengguna Laravel yang tidak tahu bagaimana cara membuat manual authentication. Hal ini meliputi bagaimana membuat multiple authentication dalam sebuah aplikasi yang sedang dikerjakannya.
Baca Juga: Aplikasi E-Commerce Laravel 6 #9: Customer Transactions
Set Customer Password
Pada seri sebelumnya terdapat sebuah schema dimana customer akan secara otomatis didaftarkan ketika mereka melakukan transaksi, hanya saja kita melupakan satu hal, yakni men-generate password untuk customer tersebut. Field untuk menampung password pada table customers
juga tidak tersedia, maka langkah pertama adalah membuat field tersebut. Pada command line jalankan
php artisan make:migration add_field_password_to_customers_table
Buka file migration yang baru saja di-generate dan modifikasi menjadi.
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddFieldPasswordToCustomersTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('customers', function (Blueprint $table) {
$table->string('password')->after('email');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('customers', function (Blueprint $table) {
$table->dropColumn('password');
});
}
}
Migration lainnya adalah untuk menampung token aktivasi pendaftaran user, pada command line, jalankan command
php artisan make:migration add_field_active_token_to_customers_table
Sebenarnya bisa saja disatukan dengan migration sebelumnya, hanya saja saya terlanjut membuatnya maka kita lanjutkan saja, buka file migration yang baru saja di-generate dan modifikasi menjadi
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddFieldActiveTokenToCustomersTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('customers', function (Blueprint $table) {
$table->string('activate_token')->after('district_id')->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('customers', function (Blueprint $table) {
$table->dropColumn('activate_token');
});
}
}
Selanjutnya adalah menambahkan random password dan activate token ke dalam data customer yang akan disimpan, buka file CartController.php
dan modifikasi query untuk menambahkan data customer yang berada pada method processCheckout()
$password = Str::random(8); //TAMBAHKAN LINE INI
$customer = Customer::create([
'name' => $request->customer_name,
'email' => $request->email,
'password' => $password, //TAMBAHKAN LINE INI
'phone_number' => $request->customer_phone,
'address' => $request->customer_address,
'district_id' => $request->district_id,
'activate_token' => Str::random(30), //TAMBAKAN LINE INI
'status' => false
]);
Kemudian kita buat mutator untuk meng-encrypt password tersebut sebelum disimpan ke database, buka file Customer.php
dan tambahkan method berikut
public function setPasswordAttribute($value)
{
$this->attributes['password'] = bcrypt($value);
}
Note: Mutator memiliki peran untuk mengubah value sebelum menyimpannya ke dalam database.
Send Email Verification
Tugas selanjutnya adalah mengirimkan informasi customer melalui email, tapi sebelum melanjutkan pembahasan ini, sediakan mail server yang bisa digunakan untuk mengirim email melalui smtp. Sebagai contoh, kamu bisa membuat akun di Mailgun.
Saya asumsikan bahwa kamu sudah memiliki smtp account, maka kita akan melanjutkan tugas kita yakni membuat service untuk mengirimkan email. Pada command line, jalankan command
php artisan make:mail CustomerRegisterMail
Sebuah file baru akan di-generate dan disimpan di dalam folder app/Mail
, buka file CustomerRegisterMail.php
yang ada di dalamnya dan modifikasi menjadi.
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use App\Customer; //USE STATEMENT MODEL CUSTOMER
class CustomerRegisterMail extends Mailable
{
use Queueable, SerializesModels;
/**
* Create a new message instance.
*
* @return void
*/
protected $customer;
protected $randomPassword;
//MEMINTA DATA BERUPA INFORMASI CUSTOMER DAN RANDOM PASSWORD YANG BELUM DI-ENCRYPT
public function __construct(Customer $customer, $randomPassword)
{
$this->customer = $customer;
$this->randomPassword = $randomPassword;
}
/**
* Build the message.
*
* @return $this
*/
public function build()
{
//MENGESET SUBJECT EMAIL, VIEW MANA YANG AKAN DI-LOAD DAN DATA APA YANG AKAN DIPASSING KE VIEW
return $this->subject('Verifikasi Pendaftaran Anda')
->view('emails.register')
->with([
'customer' => $this->customer,
'password' => $this->randomPassword
]);
}
}
Kemudian kita sediakan file register.blade.php
di dalam folder resources/views/email
dan tambahkan code.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Verifikasi Pendaftaran Anda</title>
</head>
<body>
<h2>Hai, {{ $customer->name }}</h2>
<p>Terima kasih telah melakukan transaksi pada aplikasi kami, berikut password anda: <strong>{{ $password }}</strong></p>
<p>Jangan lupa untuk melakukan verifikasi pendaftaran <a href="{{ route('customer.verify', $customer->activate_token) }}">DISINI</a></p>
</body>
</html>
Jika kita lihat pada code di atas, terdapat route name yang digunakan. Jadi kita harus menyediakan route name tersebut untuk menghindari error. Buka file Ecommerce/FrontController.php
dan tambahkan method
public function verifyCustomerRegistration($token)
{
//JADI KITA BUAT QUERY UNTUK MENGMABIL DATA USER BERDASARKAN TOKEN YANG DITERIMA
$customer = Customer::where('activate_token', $token)->first();
if ($customer) {
//JIKA ADA MAKA DATANYA DIUPDATE DENGNA MENGOSONGKAN TOKENNYA DAN STATUSNYA JADI AKTIF
$customer->update([
'activate_token' => null,
'status' => 1
]);
//REDIRECT KE HALAMAN LOGIN DENGAN MENGIRIMKAN FLASH SESSION SUCCESS
return redirect(route('customer.login'))->with(['success' => 'Verifikasi Berhasil, Silahkan Login']);
}
//JIKA TIDAK ADA, MAKA REDIRECT KE HALAMAN LOGIN
//DENGAN MENGIRIMKAN FLASH SESSION ERROR
return redirect(route('customer.login'))->with(['error' => 'Invalid Verifikasi Token']);
}
Jangan lupa untuk menambahkan use statement
use App\Customer;
Definisikan route-nya, buka file routes/web.php
dan tambahkan code
Route::group(['prefix' => 'member', 'namespace' => 'Ecommerce'], function() {
Route::get('verify/{token}', 'FrontController@verifyCustomerRegistration')->name('customer.verify');
});
Penjelasan: Route baru ini kita group dengan menambahkan prefix /member
, jadi semua route yang ada di dalamnya url-nya akan diawali dengan prefix tersebut.
Eh ada lagi nih route name yang dipanggil tapi belum didefinisikan sebelumnya, yakni customer.login
. Buat controller baru dengan command
php artisan make:controller Ecommerce/LoginController
Kemudian buka file tersebut dan modifikasi menjadi
<?php
namespace App\Http\Controllers\Ecommerce;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class LoginController extends Controller
{
public function loginForm()
{
return view('ecommerce.login');
}
}
Perlu dong untuk kita definisikan route-nya lagi, buka file routes/web.php
dan tambahkan route berikut tepat di dalam group route diatas.
Route::group(['prefix' => 'member', 'namespace' => 'Ecommerce'], function() {
Route::get('login', 'LoginController@loginForm')->name('customer.login'); //TAMBAHKAN ROUTE INI
Route::get('verify/{token}', 'FrontController@verifyCustomerRegistration')->name('customer.verify');
});
Buat file login.blade.php
di dalam folder resources/views/ecommerce
dan tambahkan code
@extends('layouts.ecommerce')
@section('title')
<title>Login - DW Ecommerce</title>
@endsection
@section('content')
<!--================Home Banner Area =================-->
<section class="banner_area">
<div class="banner_inner d-flex align-items-center">
<div class="container">
<div class="banner_content text-center">
<h2>Login/Register</h2>
<div class="page_link">
<a href="{{ url('/') }}">Home</a>
<a href="{{ route('customer.login') }}">Login</a>
</div>
</div>
</div>
</div>
</section>
<!--================End Home Banner Area =================-->
<!--================Login Box Area =================-->
<section class="login_box_area p_120">
<div class="container">
<div class="row">
<div class="offset-md-3 col-lg-6">
@if (session('success'))
<div class="alert alert-success">{{ session('success') }}</div>
@endif
@if (session('error'))
<div class="alert alert-danger">{{ session('error') }}</div>
@endif
<div class="login_form_inner">
<h3>Log in to enter</h3>
<form class="row login_form" action="contact_process.php" method="post" id="contactForm" novalidate="novalidate">
<div class="col-md-12 form-group">
<input type="email" class="form-control" id="email" name="email" placeholder="Email Address">
</div>
<div class="col-md-12 form-group">
<input type="password" class="form-control" id="password" name="password" placeholder="******">
</div>
<div class="col-md-12 form-group">
<div class="creat_account">
<input type="checkbox" id="f-option2" name="remember">
<label for="f-option2">Keep me logged in</label>
</div>
</div>
<div class="col-md-12 form-group">
<button type="submit" value="submit" class="btn submit_btn">Log In</button>
<a href="#">Forgot Password?</a>
</div>
</form>
</div>
</div>
</div>
</div>
</section>
@endsection
Semua kebutuhan dan yang saling terkait sudah tersedia, maka tugas kita yang terakhir pada sub-bab ini adalah menggunakan Mail service untuk mengirimkan email pada customer terkait. Buka file CartController
dan tambahkan code dibawah ini di dalam method processCheckout()
, tepatnya sebelum fungsi return.
DB::commit();
$carts = [];
$cookie = cookie('dw-carts', json_encode($carts), 2880);
Mail::to($request->email)->send(new CustomerRegisterMail($customer, $password)); //TAMBAHKAN CODE INI SAJA
return redirect(route('front.finish_checkout', $order->invoice))->cookie($cookie);
Jangan lupa menambahkan use statement
use App\Mail\CustomerRegisterMail;
use Mail;
Agar email pengirim dan nama pengirim lebih jelas, buka file config/mail.php
dan modifikasi code di bawah ini
'from' => [
'address' => env('MAIL_FROM_ADDRESS', '[email protected]'),
'name' => env('MAIL_FROM_NAME', 'DW Ecommerce'),
],
Eits, ada yang ketinggalan, buka file .env
dan masukkan informasi smtp mail kamu pada konfigurasi berikut
MAIL_DRIVER=smtp
MAIL_HOST=HOST SMTP ANDA
MAIL_PORT=PORT NYA BERAPA
MAIL_USERNAME=USERNAME SMTP ANDA
MAIL_PASSWORD=PASSWORD SMTP ANDA
MAIL_ENCRYPTION=tls
Agar halaman login lebih mudah diakses dari menu navigasi, buka file /layouts/ecommerce.blade.php
dan cari code di bawah ini
<ul class="right_side">
<!-- MODIFIKASI BAGIAN INI -->
<li><a href="{{ route('customer.login') }}">Login</a></li>
<!-- MODIFIKASI BAGIAN INI -->
<li><a href="#">My Account</a></li>
<li><a href="contact.html">Contact Us</a></li>
</ul>
Customer Guard Authentication
Laravel memperkenalkan istilah guard untuk proses otentikasinya, secara default guard yang digunakan adalah web
yang terhubung dengan model User.php
atau table users
. Karena kita akan menggunakan table customers
untuk mengenali user yang akan melakukan proses otentikasi, maka diperlukan sebuah guard baru.
Guard diatur di dalam sebuah konfigurasi, buka file config/auth.php
dan modifikasi menjadi
<?php
return [
/*
|--------------------------------------------------------------------------
| Authentication Defaults
|--------------------------------------------------------------------------
|
| This option controls the default authentication "guard" and password
| reset options for your application. You may change these defaults
| as required, but they're a perfect start for most applications.
|
*/
'defaults' => [
'guard' => 'web',
'passwords' => 'users',
],
/*
|--------------------------------------------------------------------------
| Authentication Guards
|--------------------------------------------------------------------------
|
| Next, you may define every authentication guard for your application.
| Of course, a great default configuration has been defined for you
| here which uses session storage and the Eloquent user provider.
|
| All authentication drivers have a user provider. This defines how the
| users are actually retrieved out of your database or other storage
| mechanisms used by this application to persist your user's data.
|
| Supported: "session", "token"
|
*/
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
/* TAMBAHKAN CODE INI */
'customer' => [
'driver' => 'session',
'provider' => 'customers',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
'hash' => false,
],
],
/*
|--------------------------------------------------------------------------
| User Providers
|--------------------------------------------------------------------------
|
| All authentication drivers have a user provider. This defines how the
| users are actually retrieved out of your database or other storage
| mechanisms used by this application to persist your user's data.
|
| If you have multiple user tables or models you may configure multiple
| sources which represent each model / table. These sources may then
| be assigned to any extra authentication guards you have defined.
|
| Supported: "database", "eloquent"
|
*/
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\User::class,
],
/* TAMBAHKAN CODE INI */
'customers' => [
'driver' => 'eloquent',
'model' => App\Customer::class,
],
// 'users' => [
// 'driver' => 'database',
// 'table' => 'users',
// ],
],
/*
|--------------------------------------------------------------------------
| Resetting Passwords
|--------------------------------------------------------------------------
|
| You may specify multiple password reset configurations if you have more
| than one user table or model in the application and you want to have
| separate password reset settings based on the specific user types.
|
| The expire time is the number of minutes that the reset token should be
| considered valid. This security feature keeps tokens short-lived so
| they have less time to be guessed. You may change this as needed.
|
*/
'passwords' => [
'users' => [
'provider' => 'users',
'table' => 'password_resets',
'expire' => 60,
],
],
/*
|--------------------------------------------------------------------------
| Password Confirmation Timeout
|--------------------------------------------------------------------------
|
| Here you may define the amount of seconds before a password confirmation
| times out and the user is prompted to re-enter their password via the
| confirmation screen. By default, the timeout lasts for three hours.
|
*/
'password_timeout' => 10800,
];
Penjelasan: Ada dua bagian yang perlu diubah, pertama pada bagian guards
kita tambahkan sebuah array dengan key customer
dimana provider-nya merujuk pada customers
. Jadi pada bagian kedua di dalam providers
kita perlu menambahkan data array dengan key customers
. Perhatikan value dari model
adalah class Customer.php
, sehingga sampai pada tahap ini kita sudah berhasil menghubungkan antara konfigurasi di atas dengan model Customer.php.
Kemudian buka model Customer.php
dan modifikasi menjadi
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
class Customer extends Authenticatable
{
use Notifiable;
protected $guarded = [];
public function setPasswordAttribute($value)
{
$this->attributes['password'] = bcrypt($value);
}
}
Note: Kita perlu meng-extends Authenticable
agar bisa menggunakan semua fitur otentikasi yang dimilikinya.
Selanjutnya buka file Ecommerce/LoginController.php
dan tambahkan method berikut untuk meng-handle proses otentikasi
public function login(Request $request)
{
//VALIDASI DATA YANG DITERIMA
$this->validate($request, [
'email' => 'required|email|exists:customers,email',
'password' => 'required|string'
]);
//CUKUP MENGAMBIL EMAIL DAN PASSWORD SAJA DARI REQUEST
//KARENA JUGA DISERTAKAN TOKEN
$auth = $request->only('email', 'password');
$auth['status'] = 1; //TAMBAHKAN JUGA STATUS YANG BISA LOGIN HARUS 1
//CHECK UNTUK PROSES OTENTIKASI
//DARI GUARD CUSTOMER, KITA ATTEMPT PROSESNYA DARI DATA $AUTH
if (auth()->guard('customer')->attempt($auth)) {
//JIKA BERHASIL MAKA AKAN DIREDIRECT KE DASHBOARD
return redirect()->intended(route('customer.dashboard'));
}
//JIKA GAGAL MAKA REDIRECT KEMBALI BERSERTA NOTIFIKASI
return redirect()->back()->with(['error' => 'Email / Password Salah']);
}
Definisikan route-nya, buka file routes/web.php
dan tambahkan route berikut di dalam group dengan prefix member
.
Route::post('login', 'LoginController@login')->name('customer.post_login');
Buka file resources/views/ecommerce/login.blade.php
dan modifikasi tepat dibagian tag form dengan menambahkan action route-nya menjadi
<form class="row login_form" action="{{ route('customer.post_login') }}" method="post" id="contactForm" novalidate="novalidate">
@csrf
Sebelum melakukan proses testing, kita perlu menyediakan halaman dashboard terlebih dahulu. Buka file Ecommerce/LoginController.php
dan tambahkan method
public function dashboard()
{
return view('ecommerce.dashboard');
}
Kemudian buat file dashboard.blade.php
di dalam folder resources/views/ecommerce
dan tambahkan code berikut
@extends('layouts.ecommerce')
@section('title')
<title>Dashboard - DW Ecommerce</title>
@endsection
@section('content')
<!--================Home Banner Area =================-->
<section class="banner_area">
<div class="banner_inner d-flex align-items-center">
<div class="container">
<div class="banner_content text-center">
<h2>Dashboard</h2>
<div class="page_link">
<a href="{{ url('/') }}">Home</a>
<a href="{{ route('customer.dashboard') }}">Dashboard</a>
</div>
</div>
</div>
</div>
</section>
<!--================End Home Banner Area =================-->
<!--================Login Box Area =================-->
<section class="login_box_area p_120">
<div class="container">
<div class="row">
<div class="col-lg-12">
<h1 class="text-center">Dashboard Sementara</h1>
</div>
</div>
</div>
</section>
@endsection
Terakhir definisikan route-nya dengan menambahkan line code berikut di dalam route group dengan prefix member
Route::group(['middleware' => 'customer'], function() {
Route::get('dashboard', 'LoginController@dashboard')->name('customer.dashboard');
});
Note: Kita menggunakan group lagi di dalam group, bedanya group ini akan menerapkan middleware customer untuk mengecek apakah customer terkait sudah login atau belum.
Middleware customer
didapatkan dari mana? Dia adalah sebuah middleware baru, maka buat middleware dengan command
php artisan make:middleware CustomerAuthenticate
File tersebut akan disimpan ke dalam folder app/Http/Middleware
, buka file CustomerAuthenticate.php
dan modifikasi menjadi
<?php
namespace App\Http\Middleware;
use Closure;
class CustomerAuthenticate
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
//JADI KITA CEK, JIKA GUARD CUSTOMER BELUM LOGIN
if (!auth()->guard('customer')->check()) {
//MAKA REDIRECT KE HALAMAN LOGIN
return redirect(route('customer.login'));
}
//JIKA SUDAH MAKA REQUEST YANG DIMINTA AKAN DISEDIAKAN
return $next($request);
}
}
Note: Jadi middleware bekerja ketika request diberikan oleh user, maka prosesnya akan dijalankan.
Daftarkan middleware baru tersebut agar bisa digunakan, buka file app/Http/Kernel.php
dan tambahkan line berikut di dalam protected $routeMiddleware
'customer' => \App\Http\Middleware\CustomerAuthenticate::class,
Sebagai penutup, kita akan membuat tombol logout. Buka file Ecommerce/LoginController.php
dan tambahkan method berikut
public function logout()
{
auth()->guard('customer')->logout(); //JADI KITA LOGOUT SESSION DARI GUARD CUSTOMER
return redirect(route('customer.login'));
}
Definisikan route-nya dengan menambahkan code berikut di dalam group yang menggunakan middleware customer
, karena fungsi ini hanya bisa diakses ketika user sudah login.
Route::group(['middleware' => 'customer'], function() {
Route::get('dashboard', 'LoginController@dashboard')->name('customer.dashboard');
Route::get('logout', 'LoginController@logout')->name('customer.logout'); //TAMBAHKAN BARIS INI
});
Masih ingat dengan tombol Login yang kita modifikasi pada header halaman? Mari kita modifikasi lagi. Buka file resources/views/layouts/ecommerce.blade.php
dah ubah code-nya menjadi
<ul class="right_side">
@if (auth()->guard('customer')->check())
<li><a href="{{ route('customer.logout') }}">Logout</a></li>
@else
<li><a href="{{ route('customer.login') }}">Login</a></li>
@endif
<li><a href="#">My Account</a></li>
<li><a href="contact.html">Contact Us</a></li>
</ul>
Penjelasan: Jadi tombol logout yang akan ditampilkan jika user terkait sudah login, jika belum maka yang akan ditampilkan adalah tombol login.
Agar user tidak bisa mengakses halaman login ketika dia sudah login, maka buka file Ecommerce/LoginController.php
dan modifikasi method berikut menjadi
public function loginForm()
{
if (auth()->guard('customer')->check()) return redirect(route('customer.dashboard'));
return view('ecommerce.login');
}
Baca Juga: Aplikasi E-Commerce Laravel 6 #8: Manage Carts
Kesimpulan
Otentikasi adalah gerbang masuk dan keluar dari sebuah batasan yang diterapkan untuk para pengguna. Pada seri ini kita telah belajar membuat multiple authentication di Laravel atau lebih tepatnya kita membuat fungsi otentikasi secara custom tanpa menggunakan bawaan Laravel. Jadi sekarang kita memiliki dua buah guard yakni web
secara default dan customer
.
Dokumentasi code dari artikel ini bisa dilihat di Github.
Comments