📖 CodeIgniter 4 Framework - Validation, Security và Sessions
65 phút

Validation, Security và Sessions

Validation

Controller validation

public function store()
{
    $rules = [
        'name' => [
            'label' => 'Product Name',
            'rules' => 'required|min_length[2]|max_length[200]',
            'errors' => [
                'required' => 'Vui lòng nhập tên sản phẩm',
                'min_length' => 'Tên phải có ít nhất 2 ký tự',
            ],
        ],
        'email' => 'required|valid_email|is_unique[users.email]',
        'price' => 'required|numeric|greater_than[0]',
        'image' => [
            'rules' => 'uploaded[image]|max_size[image,2048]|is_image[image]|mime_in[image,image/jpg,image/jpeg,image/png]',
        ],
    ];

    if (!$this->validate($rules)) {
        return redirect()->back()->withInput()
            ->with('errors', $this->validator->getErrors());
    }

    // Valid data
    $data = $this->validator->getValidated();
    // ...
}

Custom validation rules

// app/Validation/MyRules.php
namespace App\Validation;

class MyRules
{
    public function even(string $value): bool
    {
        return ((int) $value) % 2 === 0;
    }

    public function phone(string $value): bool
    {
        return preg_match('/^[0-9]{10,11}$/', $value) === 1;
    }
}

// Sử dụng
$rules = [
    'quantity' => 'required|even',
    'phone' => 'required|phone',
];

Security

CSRF Protection

// Tự động khi dùng form helper
<?= form_open('products/create') ?>
    <?= csrf_field() ?>
    <input type="text" name="name">
    <button type="submit">Save</button>
<?= form_close() ?>

XSS Protection

// Escape output
<?= esc($data) ?>
<?= esc($data, 'html') ?>

// Trong controller
$data = $this->request->getPost('comment', FILTER_SANITIZE_FULL_SPECIAL_CHARS);

Password Hashing

$hash = password_hash($password, PASSWORD_DEFAULT);
password_verify($input, $hash);

SQL Injection Prevention

// Query Builder (an toàn)
$db->table('users')->where('email', $email)->get();

// Query bindings
$db->query('SELECT * FROM users WHERE email = ?', [$email]);
$db->query('SELECT * FROM users WHERE email = :email:', ['email' => $email]);

Filters

Tạo Filter

php spark make:filter AuthFilter

AuthFilter

<?php

namespace App\Filters;

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

class AuthFilter implements FilterInterface
{
    public function before(RequestInterface $request, $arguments = null)
    {
        if (!session()->get('user_id')) {
            return redirect()->to('/login');
        }
    }

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

Register filter trong Routes

$routes->group('admin', ['filter' => 'auth'], function ($routes) {
    $routes->get('dashboard', 'Admin\Dashboard::index');
    $routes->get('users', 'Admin\Users::index');
});

Config Filters

// app/Config/Filters.php
public array $aliases = [
    'csrf' => \CodeIgniter\Filters\CSRF::class,
    'auth' => \App\Filters\AuthFilter::class,
    'admin' => \App\Filters\AdminFilter::class,
];

public array $globals = [
    'before' => [
        'csrf' => ['except' => ['api/*']],
    ],
];

Sessions

Sử dụng Session

$session = session();

// Set
$session->set('user_id', 123);
$session->set([
    'name' => 'John',
    'role' => 'admin',
]);

// Get
$userId = $session->get('user_id');
$name = $session->get('name') ?? 'Guest';

// Check
if ($session->has('user_id')) { }

// Remove
$session->remove('user_id');

// Destroy all
$session->destroy();

// Flash data (1 lần)
$session->setFlashdata('success', 'Saved!');
// Trong view
<?php if (session()->has('success')): ?>
    <div class="alert"><?= session('success') ?></div>
<?php endif; ?>

Login System

<?php

namespace App\Controllers;

class Auth extends BaseController
{
    public function login()
    {
        return view('auth/login');
    }

    public function attemptLogin()
    {
        $rules = [
            'email' => 'required|valid_email',
            'password' => 'required',
        ];

        if (!$this->validate($rules)) {
            return redirect()->back()->withInput()
                ->with('errors', $this->validator->getErrors());
        }

        $email = $this->request->getPost('email');
        $password = $this->request->getPost('password');

        $model = new \App\Models\UserModel();
        $user = $model->where('email', $email)->first();

        if (!$user || !password_verify($password, $user['password'])) {
            return redirect()->back()->withInput()
                ->with('error', 'Invalid credentials');
        }

        session()->set([
            'user_id' => $user['id'],
            'user_name' => $user['name'],
            'is_logged_in' => true,
        ]);

        return redirect()->to('/dashboard');
    }

    public function logout()
    {
        session()->destroy();
        return redirect()->to('/login');
    }
}

CORS cho API

<?php

namespace App\Filters;

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

class CorsFilter implements FilterInterface
{
    public function before(RequestInterface $request, $arguments = null)
    {
        header('Access-Control-Allow-Origin: *');
        header('Access-Control-Allow-Headers: Content-Type, Authorization');
        header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');

        if ($request->getMethod() === 'options') {
            exit(0);
        }
    }

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

Bài tập thực hành

Hãy implement login system với session!

📝 Bài tập (1)

  1. Implement login/logout với filter