Laravel 6 Rest API Menggunakan Otentikasi JWT - CRUDPRO

Laravel 6 Rest API Menggunakan Otentikasi JWT

Laravel 6 Rest API menggunakan Otentikasi JWT

Otentikasi JWT sediakan langkah yang aman untuk mengirim data di antara konsumen dan server memakai Laravel API. Dalam posting ini, kita akan menyaksikan bagaimana kita bisa membuat autentikasi JWT untuk API berbasiskan Laravel.

API sediakan antar-muka yang lancar untuk berbicara dengan beragam tipe service. Bila Anda coba membuat program yang ingin Anda beri ke pemakai akhir pada basis yang lain seperti situs, iOS atau Android, karena itu memakai pendekatan API akan bagus karena Anda bisa membuat code program frontend yang lain tanpa mengganti code backend Anda.

Saat sebelum masuk ke JWT, yok cari info dahulu apakah itu JWT?

Apakah itu Token Situs JSON?

JWT ialah ringkasan dari JSON Situs Token dan sebagai standard terbuka untuk mentransmisikan info secara aman di antara beragam faksi (client dan server) sebagai object JSON (JavaScript Objek Notation).

Di JWT, informasi dapat dipercaya atau diverifikasi berdasarkan tanda tangan digital yang dibawanya. Biasanya, kami menggunakan token yang ditandatangani untuk memverifikasi informasi.

Kapan Menggunakan Token Web JSON

Ada berbagai skenario di mana Anda dapat menggunakan JWT untuk mentransfer data atau informasi dengan lebih aman. Salah satu penggunaan JWT yang paling populer adalah dalam otorisasi API. Setelah pengguna berhasil masuk, semua permintaan selanjutnya akan menyertakan JWT yang dapat digunakan untuk mengakses rute, layanan, dan hanya mengizinkan sumber daya yang Anda inginkan dapat diakses.

Penggunaan lain dari otentikasi JWT adalah untuk bertukar/berbagi informasi adalah cara aman yang baik. Karena JWT dapat ditandatangani menggunakan pasangan kunci publik/pribadi – Anda dapat yakin bahwa pengirim yang membuat permintaan dipercaya.

Mengapa Anda Harus Menggunakan JWT?

Laravel menggunakan sistem otentikasi berbasis sesi yang keluar dari kotak saat Anda membuat aplikasi baru. Jika Anda melihat di file config/auth.php, Anda dapat mengetahui bahwa secara default konfigurasi otentikasi Laravel.

/*
|--------------------------------------------------------------------------
| 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',
    ],

    'api' => [
        'driver' => 'token',
        'provider' => 'users',
        'hash' => false,
    ],
],

Mekanisme otentikasi Laravel didasarkan pada penjaga dan penyedia. Gurad menentukan bagaimana pengguna diautentikasi untuk setiap permintaan, sementara penyedia menentukan konfigurasi penjaga. Secara default Laravel memanfaatkan sesi dan cookie.

Sesi atau cookie sangat bagus untuk autentikasi saat aplikasi klien dan server Anda berada di domain yang sama. Namun dalam kasus API, Anda cenderung memiliki klien di domain yang berbeda dan aplikasi backend Anda di domain terpisah.

Dalam hal ini, Anda akan berakhir dengan kesalahan Cross Origin Resource Sharing (CORS).

Saat membangun aplikasi di lingkungan komersial, merupakan praktik umum untuk menggunakan JSON Web Tokens (JWT), yang berarti jika Anda menggunakan Laravel 6 maka Anda harus memperbarui konfigurasi penjaga dan menggunakan JWT sebagai ganti sesi.

Dari bagian selanjutnya, kita akan mulai dengan menginstal Laravel dan membuat API yang akan menggunakan autentikasi JWT.

Perencanaan Aplikasi

Sebelum kita memulai coding kita perlu menentukan roadmap yang akan kita bangun di postingan ini. Kami akan membangun API untuk Manajemen Tugas dengan Otentikasi JWT.

  • Seorang pengguna dapat membuat akun baru.
  • Seorang pengguna dapat masuk ke akun mereka.
  • Pengguna dapat keluar untuk membuang token otentikasi.
  • Dapatkan semua tugas yang terkait dengan Pengguna.
  • Temukan tugas tertentu berdasarkan Id.
  • Tambahkan tugas baru ke daftar tugas Pengguna.
  • Edit detail tugas yang ada.
  • Hapus tugas yang ada dari daftar pengguna.

Berikut adalah dua model Eloquent Pengguna dan Tugas.

Seorang Pengguna akan membutuhkan:

  • nama
  • surel
  • kata sandi

Sebuah Tugas akan membutuhkan:

  • judul
  • keterangan
  • identitas pengguna

Membuat Aplikasi Laravel 6 Baru

Mari kita mulai dengan membuat aplikasi Laravel 6 baru. Jalankan perintah di bawah ini pada terminal baris perintah untuk menghasilkan aplikasi Laravel baru.

composer create-project laravel/laravel JWTApp

Ini akan membuat aplikasi Laravel baru di folder bernama JWTApp. Jika Anda menghadapi masalah dalam menginstal Laravel, silakan lihat postingan kami di Instal Laravel di Windows atau Linux.

Menginstal Paket Autentikasi JWT

Setelah Anda membuat aplikasi Laravel, kami akan menginstal paket tymondesigns/jwt-auth untuk bekerja dengan otentikasi JWT di Laravel.

Jalankan perintah di bawah ini di terminal untuk menginstal paket ini.

composer require tymon/jwt-auth:dev-develop --prefer-source

Ketika penginstalan paket selesai, jalankan perintah di bawah ini untuk menerbitkan konfigurasi paket.

php artisan vendor:publish

Perintah di atas akan memberi Anda daftar semua paket yang dapat ditemukan, pilih Penyedia: Tymon\JWTAuth\Providers\LaravelServiceProvider dari daftar dan tekan enter.

λ php artisan vendor:publish

 Which provider or tag's files would you like to publish?:
  [0 ] Publish files from all providers and tags listed below
  [1 ] Provider: Facade\Ignition\IgnitionServiceProvider
  [2 ] Provider: Fideloper\Proxy\TrustedProxyServiceProvider
  [3 ] Provider: Illuminate\Foundation\Providers\FoundationServiceProvider
  [4 ] Provider: Illuminate\Mail\MailServiceProvider
  [5 ] Provider: Illuminate\Notifications\NotificationServiceProvider
  [6 ] Provider: Illuminate\Pagination\PaginationServiceProvider
  [7 ] Provider: Laravel\Tinker\TinkerServiceProvider
  [8 ] Provider: Tymon\JWTAuth\Providers\LaravelServiceProvider
  [9 ] Tag: config
  [10] Tag: flare-config
  [11] Tag: ignition-config
  [12] Tag: laravel-errors
  [13] Tag: laravel-mail
  [14] Tag: laravel-notifications
  [15] Tag: laravel-pagination
 > 8
8

Copied File [\vendor\tymon\jwt-auth\config\config.php] To [\config\jwt.php]
Publishing complete.
Publishing complete.

Perintah di atas telah menghasilkan file konfigurasi jwt.php di folder config. Silakan buka file ini dan periksa pengaturan mana yang tersedia melalui paket ini.

Menghasilkan Kunci Otentikasi JWT

Token otentikasi JWT akan ditandatangani dengan kunci enkripsi, jalankan perintah berikut untuk menghasilkan kunci rahasia yang digunakan untuk menandatangani token.

php artisan jwt:secret
λ php artisan jwt:secret
jwt-auth secret [pUSAT5tCxJLHT28RNGMLpbgis3J6MD2NUEDJQtgeGYJgwBVLk

Mendaftarkan JWT Middleware

Paket JWT hadir dengan middleware pra-bangun yang dapat kita gunakan untuk rute API kita. Buka file app/Http/Kernel.php dan daftarkan middleware ini dengan nama auth.jwt.

/**
 * The application's route middleware.
 *
 * These middleware may be assigned to groups or used individually.
 *
 * @var array
 */
protected $routeMiddleware = [
    'auth' => \App\Http\Middleware\Authenticate::class,
    'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
    'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
    'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
    'can' => \Illuminate\Auth\Middleware\Authorize::class,
    'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
    'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
    'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
    'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
    'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
    'auth.jwt'  =>  \Tymon\JWTAuth\Http\Middleware\Authenticate::class, // JWT middleware
];

Middleware ini akan memeriksa apakah pengguna diautentikasi dengan memeriksa token yang dikirim dengan permintaan jika pengguna tidak diautentikasi, itu akan memunculkan pengecualian UnauthorizedHttpException.

Menyiapkan Rute API

Di bagian ini, kami akan mengatur rute kami yang diperlukan untuk aplikasi kami ini. Buka file route/api.php dan salin rute di bawah ke file ini.

Route::post('login', 'ApiController@login');
Route::post('register', 'ApiController@register');

Route::group(['middleware' => 'auth.jwt'], function () {
    Route::get('logout', 'ApiController@logout');

    Route::get('tasks', 'TaskController@index');
    Route::get('tasks/{id}', 'TaskController@show');
    Route::post('tasks', 'TaskController@store');
    Route::put('tasks/{id}', 'TaskController@update');
    Route::delete('tasks/{id}', 'TaskController@destroy');
});

Memperbaharui Model Pengguna

Paket JWT yang kami gunakan membutuhkan penerapan antarmuka Tymon\JWTAuth\Contracts\JWTSubject pada model Pengguna kami. Antarmuka ini memerlukan penerapan dua metode getJWTIdentifier dan getJWTCustomClaims dalam model Pengguna kami.

Buka file app/User.php dan perbarui dengan yang di bawah ini.

namespace App;

use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Tymon\JWTAuth\Contracts\JWTSubject;
class User extends Authenticatable implements JWTSubject
{
    use 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',
    ];

    /**
     * Get the identifier that will be stored in the subject claim of the JWT.
     *
     * @return mixed
     */
    public function getJWTIdentifier()
    {
        return $this->getKey();
    }

    /**
     * Return a key value array, containing any custom claims to be added to the JWT.
     *
     * @return array
     */
    public function getJWTCustomClaims()
    {
        return [];
    }
}

Membuat Permintaan Formulir Pendaftaran

Mulai sekarang, kami akan mulai mengimplementasikan logika yang diperlukan untuk API kami. Pertama, kami akan membuat permintaan formulir untuk tujuan pendaftaran. Pendaftaran pengguna akan membutuhkan nama, email, dan kata sandi. Jadi mari buat kelas permintaan formulir untuk menangani validasi ini.

Kami akan membuat kelas permintaan formulir baru RegistrationFormRequest dengan menjalankan perintah di bawah ini.

php artisan make:request RegistrationFormRequest

Ini akan membuat file RegistrationFormRequest.php di folder app/Http/Requests.

Buka kelas ini dan ganti kode dengan yang di bawah ini:

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class RegistrationFormRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            'name' => 'required|string',
            'email' => 'required|email|unique:users',
            'password' => 'required|string|min:6|max:10'
        ];
    }
}

Membuat Pengontrol API untuk Login dan Registrasi

Sekarang, kita akan membuat kelas pengontrol baru dan beri nama APIController. Jalankan perintah di bawah ini untuk menghasilkan pengontrol ini.

php artisan make:controller APIController

Ini akan menghasilkan pengontrol baru di folder app/Http/Controllers. Buka pengontrol ini dan perbarui dengan kelas pengontrol di bawah ini.

namespace App\Http\Controllers;

use JWTAuth;
use App\User;
use Illuminate\Http\Request;
use Tymon\JWTAuth\Exceptions\JWTException;
use App\Http\Requests\RegistrationFormRequest;
class APIController extends Controller
{
    /**
     * @var bool
     */
    public $loginAfterSignUp = true;

    /**
     * @param Request $request
     * @return \Illuminate\Http\JsonResponse
     */
public function login(Request $request)
    {
        $input = $request->only('email', 'password');
        $token = null;

        if (!$token = JWTAuth::attempt($input)) {
            return response()->json([
                'success' => false,
                'message' => 'Invalid Email or Password',
            ], 401);
        }

        return response()->json([
            'success' => true,
            'token' => $token,
        ]);
    }

    /**
     * @param Request $request
     * @return \Illuminate\Http\JsonResponse
     * @throws \Illuminate\Validation\ValidationException
     */
    public function logout(Request $request)
    {
        $this->validate($request, [
            'token' => 'required'
        ]);

        try {
            JWTAuth::invalidate($request->token);

            return response()->json([
                'success' => true,
                'message' => 'User logged out successfully'
            ]);
        } catch (JWTException $exception) {
            return response()->json([
                'success' => false,
                'message' => 'Sorry, the user cannot be logged out'
            ], 500);
        }
    }

    /**
     * @param RegistrationFormRequest $request
     * @return \Illuminate\Http\JsonResponse
     */
    public function register(RegistrationFormRequest $request)
    {
        $user = new User();
        $user->name = $request->name;
        $user->email = $request->email;
        $user->password = bcrypt($request->password);
        $user->save();

        if ($this->loginAfterSignUp) {
            return $this->login($request);
        }

        return response()->json([
            'success'   =>  true,
            'data'      =>  $user
        ], 200);
    }
}

Di kelas pengontrol di atas, pertama-tama kami menambahkan semua kelas yang diperlukan. Kemudian kita telah mendefinisikan properti publik $loginAfterSignUp yang akan kita gunakan dalam metode register() kita.

Pertama, fungsi yang kita tambahkan adalah login(). Pada fungsi ini, pertama-tama kita mendapatkan subset data dari form request yang hanya berisi email dan password. Dengan menggunakan metode JWTAuth::attempt($input), kami menentukan apakah otentikasi berhasil dan menyimpan respons dalam variabel $token. Jika responsnya salah, maka kami mengirimkan kembali pesan kesalahan dalam format JSON. Jika autentikasi mengembalikan true maka kami mengirim respons sukses bersama dengan $token.

Selanjutnya, kami menambahkan metode logout() yang membatalkan token. Pertama, kami mendapatkan token dari permintaan formulir dan memvalidasinya, lalu memanggil metode JWTAuth::invalidate() dengan meneruskan token dari permintaan formulir. Jika responsnya benar, kami mengembalikan pesan sukses. Jika ada pengecualian yang terjadi maka kami mengirimkan kembali pesan kesalahan.

Di final, metode register() kami mendapatkan data dari permintaan formulir dan membuat instance baru dari model Pengguna dan menyimpannya. Kemudian kami memeriksa apakah properti publik kami $loginAfterSignup disetel, kami memanggil metode login() untuk mengautentikasi pengguna dan mengirim respons sukses kembali.

Model Tugas dan Migrasi

Sejauh ini, kami telah mengimplementasikan bagian otentikasi, sekarang kami akan membuat Task model baru dan melakukan operasi CRUD di atasnya.

php artisan make:model Task -mc

Perintah di atas akan membuat file migrasi baru di folder database/migrations. Buka itu dan perbarui dengan yang di bawah ini.

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateTasksTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('tasks', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->unsignedBigInteger('user_id');
            $table->string('title');
            $table->text('description')->nullable();
            $table->foreign('user_id')
                    ->references('id')
                    ->on('users')
                    ->onDelete('cascade');

            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('tasks');
    }
}
php artisan migrate

Sekarang, buka model Tugas dari app/ folder dan perbarui model dengan yang di bawah ini.

namespace App;

use Illuminate\Database\Eloquent\Model;

class Task extends Model
{
    /**
     * @var string
     */
    protected $table = 'tasks';

    /**
     * @var array
     */
    protected $guarded = [];
}
/**
 * @return \Illuminate\Database\Eloquent\Relations\HasMany
 */
public function tasks()
{
    return $this->hasMany(Task::class);
}

Pengontrol Tugas

Pada bagian ini, kami akan mengimplementasikan fungsionalitas CRUD untuk model Tugas kami. Buka TaskController dari folder app/Http/Controllers dan perbarui dengan yang di bawah ini.

namespace App\Http\Controllers;

use JWTAuth;
use App\Task;
use Illuminate\Http\Request;

class TaskController extends Controller
{
    /**
     * @var
     */
    protected $user;

    /**
     * TaskController constructor.
     */
    public function __construct()
    {
        $this->user = JWTAuth::parseToken()->authenticate();
    }
}

Dalam kode di atas, kami telah mendefinisikan properti yang dilindungi dan dengan bantuan metode __construct() atur pengguna yang saat ini diautentikasi ke properti $user.

Metode parseToken() akan mendapatkan token dari objek permintaan dan metode authenticate() akan mengautentikasi pengguna dengan token.

Selanjutnya, kita akan menambahkan metode index() yang akan mengembalikan semua tugas untuk pengguna yang saat ini diautentikasi.

/**
 * @return mixed
 */
public function index()
{
    $tasks = $this->user->tasks()->get(['title', 'description'])->toArray();

    return $tasks;
}/**
 * @return mixed
 */
public function index()
{
    $tasks = $this->user->tasks()->get(['title', 'description'])->toArray();

    return $tasks;
}

Dalam metode di atas, kami mendapatkan semua tugas untuk pengguna dan mengonversinya menjadi array.

Selanjutnya, tambahkan metode baru bernama show() yang akan mengambil tugas dengan idnya.

/**
 * @param $id
 * @return \Illuminate\Http\JsonResponse
 */
public function show($id)
{
    $task = $this->user->tasks()->find($id);

    if (!$task) {
        return response()->json([
            'success' => false,
            'message' => 'Sorry, task with id ' . $id . ' cannot be found.'
        ], 400);
    }

    return $task;
}

Contoh di atas cukup sederhana untuk dipahami. Kami hanya menemukan tugas dengan id. Jika tugas tidak ada, respons kegagalan 400 dikembalikan. Jika tidak, tugas dikembalikan.

Sekarang, tambahkan metode store() yang akan bertanggung jawab menyimpan tugas untuk pengguna.

/**
 * @param Request $request
 * @return \Illuminate\Http\JsonResponse
 * @throws \Illuminate\Validation\ValidationException
 */
public function store(Request $request)
{
    $this->validate($request, [
        'title' => 'required',
        'description' => 'required',
    ]);

    $task = new Task();
    $task->title = $request->title;
    $task->description = $request->description;

    if ($this->user->tasks()->save($task))
        return response()->json([
            'success' => true,
            'task' => $task
        ]);
    else
        return response()->json([
            'success' => false,
            'message' => 'Sorry, task could not be added.'
        ], 500);
}

Mari terapkan metode pembaruan.

/**
 * @param Request $request
 * @param $id
 * @return \Illuminate\Http\JsonResponse
 */
public function update(Request $request, $id)
{
    $task = $this->user->tasks()->find($id);

    if (!$task) {
        return response()->json([
            'success' => false,
            'message' => 'Sorry, task with id ' . $id . ' cannot be found.'
        ], 400);
    }

    $updated = $task->fill($request->all())->save();

    if ($updated) {
        return response()->json([
            'success' => true
        ]);
    } else {
        return response()->json([
            'success' => false,
            'message' => 'Sorry, task could not be updated.'
        ], 500);
    }
}

Dalam metode pembaruan, kami menemukan tugas dengan id. Jika tugas tidak ada, respons 400 dikembalikan. Kemudian kami memperbarui tugas dengan data yang ada dalam permintaan dengan menggunakan metode fill(). Model tugas yang diperbarui kemudian disimpan dalam database. Jika catatan berhasil diperbarui, respons sukses 200 dikembalikan.

Jika tidak, respons kesalahan server internal 500 dikembalikan.

/**
 * @param $id
 * @return \Illuminate\Http\JsonResponse
 */
public function destroy($id)
{
    $task = $this->user->tasks()->find($id);

    if (!$task) {
        return response()->json([
            'success' => false,
            'message' => 'Sorry, task with id ' . $id . ' cannot be found.'
        ], 400);
    }

    if ($task->delete()) {
        return response()->json([
            'success' => true
        ]);
    } else {
        return response()->json([
            'success' => false,
            'message' => 'Task could not be deleted.'
        ], 500);
    }
}

Dalam metode di atas, kami menemukan tugas dengan id. Jika tugas tidak ada, respons 400 dikembalikan. Kemudian kami menghapus tugas dan mengembalikan respons yang sesuai.

Itu saja untuk TaskController, kami telah berhasil mengimplementasikan tugas CRUD. Sekarang saatnya untuk pengujian.

Pengujian API

Untuk menguji API kami, kami akan menjalankan server PHP bawaan. Buka terminal baris perintah dan jalankan perintah di bawah ini.

php artisan serve

Ini akan memulai server pengembangan di localhost:8000.

Untuk menguji api REST ini saya akan menggunakan Postman yang merupakan lingkungan kolaboratif untuk membangun API. Anda dapat membaca lebih lanjut tentang aplikasi ini di situs web mereka.

Saya akan menguji fungsionalitas pendaftaran terlebih dahulu dan akan mencoba mendaftarkan pengguna dengan mengisi nama, email, dan kata sandi di badan permintaan.

Laravel 6 Rest API Menggunakan Otentikasi JWT

Kirim permintaan dan Anda akan mendapatkan token kembali seperti di bawah ini.

Laravel 6 Rest API Menggunakan Otentikasi JWT

Sekarang pengguna kami terdaftar menggunakan REST API. Sekarang kita akan menguji fungsionalitas login dengan mengirimkan permintaan ke rute /api/login.

Laravel 6 Rest API Menggunakan Otentikasi JWT

Ini akan mengembalikan respons 200 dengan token seperti di bawah ini.

Laravel 6 Rest API Menggunakan Otentikasi JWT

Fungsi logout akan berfungsi seperti yang diharapkan, Anda dapat mencobanya sendiri.

Selanjutnya, kami akan membuat tugas baru untuk pengguna yang saat ini diautentikasi.

Laravel 6 Rest API Menggunakan Otentikasi JWT

Sekarang kita akan mendapatkan semua tugas dengan mengunjungi rute /api/tasks dan meneruskan token JWT.

Laravel 6 Rest API Menggunakan Otentikasi JWT

Seperti yang Anda lihat, ini berfungsi seperti yang diharapkan. Saya akan meninggalkan metode lain untuk Anda uji. Jika Anda menghadapi masalah, beri tahu kami di kotak komentar di bawah.

Akhir Kata

Dalam posting ini, kami telah berhasil mengimplementasikan REST API dengan otentikasi JWT.

Anda dapat menemukan basis kode tutorial ini di repositori Laravel 6 JWT.

Jika Anda memiliki pertanyaan, beri tahu kami di kotak komentar di bawah.