Membuat Fitur Login dan Register CodeIgniter 4

irfan-photo irfan · 2 bulan lalu

Membuat Fitur Login dan Register CodeIgniter 4

melanjutkan dari praktek sebelumnya, praktek yang kedua ini saya akan membuat fitur login dan register agar nantinya halaman produk tidak bisa diakses begitu saja

berikut spesifikasi kode dari fitur login dan register yang akan dibuat

  • Menggunakan Ajax
  • Validasi Data
  • Menggunakan Filter sebagai Middleware
  • Fitur Remember me (Login otomatis)
  • Membuat 2 Halaman yaitu login dan register

Persiapan

karena fitur login dan register ini menggunakan file dari tutorial sebelumnya, maka pastikan kalian sudah membaca dan mempraktekannya

baca membuat crud codeigniter 4

Membuat Table

Langkah 1 : buat table user menggunakan migration

ketikan perintah spark

php spark make:migration user

Langkah 2 : membangun struktur table user

edit file migration user yang telah dibuat terletak di 'app/Database/Migrations/DATETIME_User.php' ganti dengan kode dibawah ini

<?php

namespace App\Database\Migrations;

use CodeIgniter\Database\Migration;

class User extends Migration
{
    public function up()
    {
        $this->forge->addField([
            'id'          => [
                'type'           => 'INT',
                'constraint'     => 100,
                'unsigned'       => true,
                'auto_increment' => true,
            ],
            'username'       => [
                'type'       => 'VARCHAR',
                'constraint' => '30',
            ],
            'password' => [
                'type' => 'VARCHAR',
                'constraint' => '200',
            ],
            'email' => [
                'type' => 'VARCHAR',
                'constraint' => '100',
            ],
            'created_at' => [
                'type' => 'DATETIME',
            ],
            'updated_at' => [
                'type' => 'DATETIME',
            ]
            ]);
        $this->forge->addKey('id', true);
        $this->forge->createTable('user');
    }
 
    public function down()
    {
        $this->forge->dropTable('user');
    }
}
Langkah 3 : menjalankan migration menggunakan spark

ketikan perintah spark berikut

php spark migrate

Membuat Controller

ketikan perintah spark

php spark make:controller auth

edit kode controller auth yang terletak di 'app/Controllers/Auth.php' dengan kode dibawah ini

<?php

namespace App\Controllers;

use App\Controllers\BaseController;

class Auth extends BaseController
{
    public function index_login()
    {

        $data['title'] = 'Login';

        return view('auth/login', $data);
    }

    public function login()
    {

        // validate input text
        $validationRule = [
            'identity' => [
                'rules' => 'required'
            ],
            'password' => [
                'rules' => 'required'
            ]
        ];

        if (!$this->validate($validationRule)) {
            $error = $this->validator->getErrors();
            $error_val = array_values($error);
            die(json_encode([
                'status' => false,
                'response' => $error_val[0]
            ])); 
        }       

        // input data
        $identity = $this->request->getPost('identity');
        $password = $this->request->getPost('password');        

        // load model
        $userModel = new \App\Models\User();

        // find user
        $user = $userModel->select('id,username,password')->where('username', $identity)->orWhere('email', $identity)->first();

        // user not found.
        if (!$user) {
            return $this->response->setJSON([
                'status' => false,
                'response' => 'Account not found'
            ]);         
        }

        // validate password
        if (!password_verify($password, $user['password'])) {
            // invalid password
            return $this->response->setJSON([
                'status' => false,
                'response' => 'Password Invalid'
            ]);     
        }

        // build data
        $data = [
            'id' => $user['id'],
            'username' => $user['username'],            
        ];

        // set session
        session()->set('auth', $data);

        // check if remember exist
        if ($this->request->getPost('remember')) {

            // load helper
            helper('aeshash');

            // set cookie
            $auth_hash = aeshash('enc', json_encode($_SESSION['auth']) , config('Encryption')->key);
            setcookie('auth', $auth_hash, time() + (86400 * 30), '/');      
        }


        // send response
        return $this->response->setJSON([
            'status' => true,
            'response' => 'Success Login',
            'redirect' => base_url('product')
        ]);     
    }

    public function index_register()
    {

        $data['title'] = 'Register';

        return view('auth/register', $data);
    }   

    public function register()
    {

        // validate input text
        $validationRule = [
            'email' => [
                'rules' => 'required|max_length[100]|valid_email|is_unique[user.email]'
            ],
            'username' => [
                'rules' => 'required|min_length[4]|max_length[30]|is_unique[user.username]'
            ],
            'password' => [
                'rules' => 'required|min_length[4]|max_length[50]'
            ],
            'password_confirm' => [
                'rules' => 'matches[password]'
            ]            
        ];

        if (!$this->validate($validationRule)) {
            $error = $this->validator->getErrors();
            $error_val = array_values($error);
            die(json_encode([
                'status' => false,
                'response' => $error_val[0]
            ])); 
        }           

        // input data
        $data['email'] = $this->request->getPost('email');        
        $data['username'] = $this->request->getPost('username');
        $data['password'] = password_hash($this->request->getPost('password'), PASSWORD_DEFAULT);

        // load model
        $userModel = new \App\Models\User();    

        // insert data
        $register = $userModel->insert($data);

        // build data
        $data = [
            'id' => $register,
            'username' => $data['username'],                
        ];

        // set session
        session()->set('auth', $data);

        // send response
        return $this->response->setJSON([
            'status' => true,
            'response' => 'Success Register',
            'redirect' => base_url('product')
        ]); 
    }     

    public function logout()
    {
        session()->remove('auth');
        setcookie('auth', null, -1, '/'); 
        return redirect()->to(base_url('login'));       
    }
}

Membuat Model

ketikan perintah spark

php spark make:model user

edit kode model user terletak di 'app/Models/User.php' dengan kode dibawah ini

<?php
 
namespace App\Models;
 
use CodeIgniter\Model;
 
class User extends Model
{
    protected $table            = 'user';
    protected $primaryKey       = 'id';
    protected $useAutoIncrement = true;
    protected $returnType       = 'array';
    protected $protectFields    = false; 

    // Dates
    protected $useTimestamps = true;
    protected $dateFormat    = 'datetime';
    protected $createdField  = 'created_at';
    protected $updatedField  = 'updated_at';    
 
}

Membuat Filter

ketikan perintah spark

php spark make:filter Auth

edit filter auth dengan kode dibawah ini

<?php

namespace App\Filters;

use CodeIgniter\Filters\FilterInterface;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;

class Auth implements FilterInterface
{

    public function before(RequestInterface $request, $arguments = null)
    {

        if (is_array($arguments) AND $arguments[0] == 'page') {
            if (session()->auth) {
                return redirect()->to(base_url('product'));       
            }
        }else{
            if (!session()->auth) {
                // if ajax request
                if ($request->isAJAX()) {
                    http_response_code(400);     
                    header('Content-Type: application/json; charset=utf-8');
                    die(json_encode([
                        'status' => false,
                        'response' => 'no authorize'                
                        ]));
                }else{

                    // if direct request
                    session()->setFlashdata('message', 'please login first');
                    return redirect()->to(base_url('login'));   
                }
            }
        }
    }

    public function after(RequestInterface $request, ResponseInterface $response, $arguments = null)
    {
        //
    }
}

dilanjut dengan membuat alias filter authnya

silahkan edit file 'app/Config/Filters.php'

tambahkan kode dibawah ini pada propery $aliases

'auth' => \App\Filters\Auth::class,

sehingga property $aliases menjadi seperti

public $aliases = [
    'csrf'          => CSRF::class,
    'toolbar'       => DebugToolbar::class,
    'honeypot'      => Honeypot::class,
    'invalidchars'  => InvalidChars::class,
    'secureheaders' => SecureHeaders::class,
    'auth' => \App\Filters\Auth::class,
];

Membuat dan Mengedit Route

disini saya akan membuat route untuk login dan registernya serta menerapkan filter yang telah dibuat sebelumnya

edit file 'app/Config/Routes.php'

silahkan diedit route product yang sudah ada menjadi seperti kode dibawah ini

// route product with filter auth
$routes->group('', ['filter' => 'auth'] ,function ($routes) {
	$routes->get('/product', 'product::index');
	$routes->get('/product/read', 'product::read');
	$routes->post('/product/create', 'product::create');
	$routes->post('/product/edit', 'product::edit');
	$routes->post('/product/update', 'product::update');
	$routes->post('/product/delete', 'product::delete');
	$routes->post('/product/delete_batch', 'product::delete_batch');
});

kemudian tambahkan route login, register, dan logout

// route auth with filter auth:page
$routes->group('', ['filter' => 'auth:page'] ,function ($routes) {
    $routes->get('/login', 'auth::index_login');
    $routes->post('/login', 'auth::login');
    $routes->get('/register', 'auth::index_register');
    $routes->post('/register', 'auth::register');
});

$routes->get('/logout', 'auth::logout');

Membuat dan Mengedit View

edit file view nav.php yang terletak di 'app/Views/_layout/nav.php' dengan kode dibawah ini

<nav class="navbar navbar-expand-md mb-4">
  <div class="container">
    <span class="navbar-brand">Store</span>
    <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarCollapse" aria-controls="navbarCollapse" aria-expanded="false" aria-label="Toggle navigation">
      <span class="navbar-toggler-icon"></span>
    </button>
    <div class="collapse navbar-collapse" id="navbarCollapse">
      <ul class="navbar-nav me-auto mb-2 mb-md-0">
        <li class="nav-item">
          <a class="nav-link" aria-current="page" href="<?= base_url()  ?>">Home</a>
        </li>
        <li class="nav-item">
          <a class="nav-link" href="<?= base_url('product')  ?>">Product</a>
        </li>
      </ul>

      <ul class="navbar-nav mb-2 mb-md-0 d-flex">
        <?php if (!session()->auth): ?>
          <li class="nav-item">
            <a class="nav-link" aria-current="page" href="<?= base_url('login')  ?>">Login</a>
          </li>
          <li class="nav-item">
            <a class="nav-link" href="<?= base_url('register')  ?>">Register</a>
          </li>
        <?php else: ?>
          <li class="nav-item">
            <a class="nav-link btn btn-outline-danger me-2" aria-current="page" href="<?= base_url('logout')  ?>">Logout</a>
          </li>
        <?php endif ?>
      </ul>

    </div>
  </div>
</nav>

dilanjut dengan membuat folder view baru dengan nama auth kemudian buat 2 file didalamnya dengan nama login.php dan register.php

sehingga nanti akan ada folder dan file baru seperti gambaran dibawah ini

.
└── app/
    └── Views/
        └── auth/
            ├── login.php
            └── register.php

buka file auth/login.php dan isi dengan kode dibawah ini

<?= $this->extend('_layout') ?>

<?= $this->section('content') ?>

<main class="py-5 my-auto">
    <div class="container">

        <div class="row justify-content-center">
            <div class="col-12 col-md-4">

                <form id="form-login" method="POST">
                    <h1 class="h3 mb-3 fw-normal">Login</h1>

                    <div id="form-message">
                        <?php if (session()->getFlashdata('message')): ?>
                            <div class="alert alert-warning">
                                <?= session()->getFlashdata('message') ?>
                            </div>                    
                        <?php endif ?>                        
                    </div>

                    <div class="form-floating mb-3">
                        <input name="identity" type="text" class="form-control" id="input-identity" placeholder="foo...">
                        <label for="input-identity">Username / Email</label>
                    </div>
                    <div class="form-floating mb-3">
                        <input name="password" type="password" class="form-control" id="input-password" placeholder="Password">
                        <label for="input-password">Password</label>
                    </div>

                    <div class="checkbox mb-3">
                        <label>
                            <input name="remember" type="checkbox" value="remember-me"> Remember me
                        </label>
                    </div>
                    
                    <button class="btn btn-lg btn-outline-dark submit-button" type="submit">Sign in</button>
                </form>

            </div>
        </div>

    </div>
</main>

<?= $this->endSection() ?>

<?= $this->section('js') ?>

<script>
    $("#form-login").on("submit", function(e){
        e.preventDefault();

        let form = $(this);

        // animation
        $("input", form).prop("readonly",true); 
        $(".submit-button").prop("disabled",true);  
        $(".submit-button",form).html($(".submit-button",form).html() + xsetting.spinner);

        let buttonspinner = $(".button-spinner");   

        $.post(base_url + `/login`, form.serialize() , {}, 'json')
        .done(function(data){

            if (data.status) {

                $("#form-message").html(`<div class="alert alert-info text-break">${data.response}</div>`);

                setTimeout(() => {
                    window.location.href = data.redirect
                },1000);

            }else{

                // animation
                $("input", form).prop("readonly",false);    
                $(".submit-button").prop("disabled",false); 
                buttonspinner.remove();


                $("#form-message").html(`<div class="alert alert-warning text-break">${data.response}</div>`);
            }

        })
        .fail(function(xhr, statusText, errorThrown) {

            alert(xhr.responseText);

            // animation
            $("input", form).prop("readonly",false);    
            $(".submit-button").prop("disabled",false); 
            buttonspinner.remove();
        })        
    })
</script>

<?= $this->endSection() ?>

buka file auth/register.php dan isi dengan kode dibawah ini

<?= $this->extend('_layout') ?>

<?= $this->section('content') ?>

<main class="py-5 my-auto">
	<div class="container">

		<div class="row justify-content-center">
			<div class="col-12 col-md-4">

				<form id="form-register" method="POST">
					<h1 class="h3 mb-3 fw-normal">Register</h1>

					<div id="form-message"></div>

					<div class="form-floating mb-3">
						<input name="email" type="email" class="form-control" id="input-email" placeholder="[email protected]">
						<label for="input-email">Email</label>
					</div>
					<div class="form-floating mb-3">
						<input name="username" type="text" class="form-control" id="input-username" placeholder="foo...">
						<label for="input-username">Username</label>
					</div>
					<div class="form-floating mb-3">
						<input name="password" type="password" class="form-control" id="input-password" placeholder="Password">
						<label for="input-password">Password</label>
					</div>
					<div class="form-floating mb-3">
						<input name="password_confirm" type="password" class="form-control" id="input-password-confirm" placeholder="Password">
						<label for="input-password-confirm">Confirm Password</label>
					</div>
					<button class="btn btn-lg btn-outline-dark submit-button" type="submit">Sign Up</button>

				</form>

			</div>
		</div>

	</div>
</main>

<?= $this->endSection() ?>

<?= $this->section('js') ?>

<script>
	$("#form-register").on("submit", function(e){
		e.preventDefault();

		let form = $(this);

        // animation
        $("input", form).prop("readonly",true); 
        $(".submit-button").prop("disabled",true);  
        $(".submit-button",form).html($(".submit-button",form).html() + xsetting.spinner);

        let buttonspinner = $(".button-spinner");   

        $.post(base_url + `/register`, form.serialize() , {}, 'json')
        .done(function(data){

        	if (data.status) {

        		$("#form-message").html(`<div class="alert alert-info text-break">${data.response}</div>`);

        		setTimeout(() => {
        			window.location.href = data.redirect
        		},1000);

        	}else{

                // animation
                $("input", form).prop("readonly",false);    
                $(".submit-button").prop("disabled",false); 
                buttonspinner.remove();

                $("#form-message").html(`<div class="alert alert-danger text-break">${data.response}</div>`);
            }

        })
        .fail(function(xhr, statusText, errorThrown) {

        	alert(xhr.responseText);

            // animation
            $("input", form).prop("readonly",false);    
            $(".submit-button").prop("disabled",false); 
            buttonspinner.remove();
        })        
    })
</script>

<?= $this->endSection() ?>

Membuat Fitur Login Remember me

fitur remember me pada login berguna agar ketika user masih dalam keadaan login lalu menutup browsernya, maka ketika browser dibuka kembali dan mengunjungi web akan login otomatis tanpa harus login lagi

kalian juga harus tau fitur remember me ini menggunakan cookie untuk menyimpan data user yang nanti akan kita deteksi ketika user keluar browser

karena fitur remember me menyimpan data user dengan cookie maka untuk mengamankannya agar tidak dihijack (membuat cookie tiruan untuk masuk) maka kita harus mengencrypt data cookienya agar tidak mudah dibaca dan dipecahkan

Langkah 1 (Membuat Helper Encrypt & Decyrpt)

buat file helper dengan nama aeshash_helper.php didalam folder 'app/Helpers/'

tambahkan kode dibawah ini pada file aeshash_helper.php terletak di 'app/Helpers/aeshash_helper.php'

<?php 

/**
* https://gist.github.com/joashp/a1ae9cb30fa533f4ad94
*/
function aeshash($action, $string, $aeskey) {
	$output = false;
	$encrypt_method = "AES-256-CBC";
	$secret_key = $aeskey;
	$secret_iv = $aeskey;
	// hash
	$key = hash('sha256', $secret_key);

	// iv - encrypt method AES-256-CBC expects 16 bytes - else you will get a warning
	$iv = substr(hash('sha256', $secret_iv), 0, 16);
	if ( $action == 'enc' ) {
		$output = openssl_encrypt($string, $encrypt_method, $key, 0, $iv);
		$output = base64_encode($output);
	} else if( $action == 'dec' ) {
		$output = openssl_decrypt(base64_decode($string), $encrypt_method, $key, 0, $iv);
	}
	return $output;
}

Langkah 2 (Mengatur Key Encription)

edit file pengaturan encryption terletak di 'app/Config/Encryption.php'

kemudian isi property $key dengan kode rahasia kalian

misalnya seperti

public $key = '[email protected]#!!((##_GREAT___+!#_';

Mendeteksi User Kehilangan session auth dan masih memiliki cookie auth

saya akan menggunakan fitur filter untuk mendeteksinya, mengingat filter ini bisa diakses secara global maka setiap mengakses url maka akan melalui filter ini terlebih dahulu

buat file filter menggunakan spark dengan mengetikan perintah

php spark make:filter AuthCookie

edit kode filter AuthCookie yang terletka di 'app/Filters/AuthCookie.php' dengan kode dibawah ini

<?php

namespace App\Filters;

use CodeIgniter\Filters\FilterInterface;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;

class AuthCookie implements FilterInterface
{

    public function before(RequestInterface $request, $arguments = null)
    {

        if (!session()->auth AND isset($_COOKIE['auth'])) {

            // load helper
            helper('aeshash');

            // decode cookie
            $hash = aeshash('dec', $_COOKIE['auth'] , config('Encryption')->key);

            // validate hash
            if (!$hash) {
                // hash invalid > remove cookie
                setcookie('auth', null, -1, '/'); 
            }else{      
                $read = json_decode($hash,true);
                session()->set('auth', $read);
            }
        }

    }

    public function after(RequestInterface $request, ResponseInterface $response, $arguments = null)
    {
        //
    }
}

dilanjut dengan mengaktifkan filternya dengan mengaturnya didalam file 'app/Config/Filters.php'

tambahkan kode dibawah ini pada propery $aliases

'authcookie' => \App\Filters\AuthCookie::class,

sehingga property $aliases menjadi seperti

public $aliases = [
    'csrf'          => CSRF::class,
    'toolbar'       => DebugToolbar::class,
    'honeypot'      => Honeypot::class,
    'invalidchars'  => InvalidChars::class,
    'secureheaders' => SecureHeaders::class,
    'auth' => \App\Filters\Auth::class,
    'authcookie' => \App\Filters\AuthCookie::class,
];

kemudian untuk mengaktifkannya filternya kita tambahkan aliases dari filter tersebut pada property $globals

tambahkan filternya dinilai before agar filter diekseskusi sebelum controller

nantinya property $globals akan menjadi seperti ini

public $globals = [
    'before' => [
        // 'honeypot',
        // 'csrf',
        // 'invalidchars',
        'authcookie'
    ],
    'after' => [
        'toolbar',
        // 'honeypot',
        // 'secureheaders',
    ],
];

Uji Coba Aplikasi

jalankan web server dengan spark

php spark serve

buka url http://localhost:8080 dibrowser kalian

dan berikut hasilnya

Download Source Code

Terakhir diupdate 08 Jun 2022 20:27 WIB
Daftar Konten Belajar CodeIgniter 4