12/08/2018, 14:19

Một vòng Laravel (Part 1)

Được hôm rảnh rỗi, ngồi ôn lại các kiến thức về laravel, nên quyết định viết luôn một bài để lưu trữ, sau này cần xem lại thì có cái để mà xem. ok, content sẽ gồm những vấn đề sau: CSRF là gì? Restful là gì? Các method cơ bản của của một Resource Controller Sự khác biệt giữa Query Builder ...

Được hôm rảnh rỗi, ngồi ôn lại các kiến thức về laravel, nên quyết định viết luôn một bài để lưu trữ, sau này cần xem lại thì có cái để mà xem.

ok, content sẽ gồm những vấn đề sau:

  • CSRF là gì?
  • Restful là gì? Các method cơ bản của của một Resource Controller
  • Sự khác biệt giữa Query Builder và Eloquent. Chúng có quan hệ gì với nhau?
  • Thế nào là Eager Loading
  • Scope
  • Accessors & Mutators
  • Middleware
  • Localization
  • Authorization (Access Control List)
  • Mail
  • Schedule
  • Event
  • Job
  • Inversion Of Control
  • Service Provider
  • Contracts
  • Facade

CSRF

CSRF (Cross-Site Request Forgery): là một hình thức tấn công tạo các Request giả mạo tới site mà người dùng đã authenticated. Cùng với XSS, SQL Injection, CSRF là 3 trong số các hình thức tấn công rất phổ biến với web application.

Cách thức thực hiện CSRF: rất đơn giản. Giả sử người dùng đã login vào 1 trang web mua sắm tiki.vn chẳng hạn. họ thực hiện mua một mặt hàng có product id = 100, gửi tới địa chỉ là hanoi, request để thực hiện việc này là GET và url có dạng: tiki.vn/buy?product_id=100&address=hanoi. Hacker bằng một cách nào đó, ví dụ gửi 1 link qua facebook cho người dùng, hoặc gửi email tới cho người dùng, content là image chứa source là "tiki.vn/buy?product_id=100&address=hacker_address". Và khi nguời dùng click vào link hoặc đọc email, vô tình họ đã thực hiện một request là mua hàng và gửi tới địa chỉ là hacker_address.

Và lưu ý kể cả nếu Request dạng POST mà không hề có hình thức xác thực hoặc bảo vệ thì cũng dễ dàng thực hiện CSRF mà thôi, bởi POST và GET chỉ khác nhau ở chỗ các param của get thì ở trên URL, còn các Param của POST thì nằm trong content của package http. Tức là giờ thay vì chỉ gửi 1 link, hacker chỉ việc gửi kèm 1 form, có các hidden field là các param thì CSRF vẫn được thực hiện.

Cách phòng tránh:

  • Với các request nhạy cảm, ví dụ như mua bán hàng, chuyển, rút tài khoản ngân hàng, ... thì nên để ở dạng POST và có 1 token string để xác nhận. Khi người dùng gửi 1 POST request, sinh ra 1 token string, ta sẽ xác nhận token string này với token đã được đính kèm vào user infor khi người dùng login. nếu trùng thì tiếp tục xử lý request, ko thôi.
  • Tạo các GET request phức tạp một chút, tức là kể cả thằng hacker có nhìn thấy thì chưa chắc nó đã nhớ được để mà lợi dụng, hoặc ít nhất là nó không đoán được :v
  • Hạn chế click và các link hay đọc các email lạ, đặc biệt là khi đang login vào hệ thống.
  • ...dùng laravel :v .Nếu bạn đang sử dụng laravel thì nó đã có các cơ chế phòng tránh rồi (sử dụng middleware VerifyCsrfToken) (và không chỉ CSRF mà laravel cũng cung cấp cơ chế protection cho cả SQL Injection và XSS attack). Nếu để ý chút thì trong Laravel Collective package, khi bạn dùng Form::open mà method = post thì nó sẽ tự sinh một hidden filed là token string để gửi lên hệ thống. token này sẽ được compare với token lưu trong session của user khi user login. nếu match thì request được next, không thì trả về cho phường xử lý. Còn nếu dùng thẻ <form></form> của html, thì khi method là post, laravel cũng yêu cầu thêm 1 hidden field là <input type="hidden" name="_token" value="{{ csrf_token() }}">.

RESTful và các method cơ bản trong resource controller

Trước khi nói về RESTful, hãy nói về REST trước. REST (REpresentational State Transfer) là kiến trúc dựa trên các chuẩn web, sử dụng các giao thức http để trao đổi dữ liệu. REST tập trung xoay quanh resource. REST coi hệ thống là tập các resource, một resource sẽ được truy cập bởi một interface chung sử dụng các HTTP standard methods (GET, POST, PUT, DELETE, OPTIONS).

Trong REST architecture, REST server sẽ cung cấp quyền truy cập tới các resource, còn REST client có nhiệm vụ truy cập, hiển thị, dữ liệu trao đổi giữa server và client đa phần ở dạng JSON. Mỗi resource đều được identify bởi một id cụ thể. Đây là một convention quan trọng để có thể tạo các url chuẩn RESTful.

RESTful web services: web services là tập các chuẩn và giao thức sư dụng cho việc trao đổi dữ liệu giữa các ứng dụng và hệ thống. RESTful web services là sự kết hợp của web services với REST architecture. web services sẽ quy định ra các operation dựa trên HTTP method.

Để cho dễ hiểu. REST architecture giống như là idea, còn RESTful là bản vẽ mô tả idea ấy (cụ thể hơn, mang tính ứng dụng cao hơn) còn system là object được tạo ra từ bản vẽ trên. Ví dụ thế này, ta có một hệ thống website bán hàng, triển khai RESTful. Hệ thống sẽ gồm server là tập các resource: product, category, customer, ... Client có thể là mobile phone, web application, tablet, ... miễn là device có thể gưi và nhận request, đều có thể giao tiếp với server này (tất nhiên sẽ phải qua vài bước security). Sau đây là một ví dụ khi triển khai resource Product.

restful_table.jpg

Các method cơ bản của của một Resource Controller: index, create, store, show, edit, update, destroy

Query Builder và Eloquent

Laravel cung cấp cho ta 2 cách thức để làm việc với Database đơn giản và dễ dàng hơn, là Query Builder (hay fluent query builder) và Eloquent

Về mối quan hệ, thì Eloquent chính là Query Builder, nói cách khác, Eloquent thực chất là một bản mở rộng của Query Builder. Đó chính là lý do các function trong Query Builder đều có thể sử dụng trong Eloquent, nhưng ngược lại thì không.

Eloquent

  • code clear, dễ đọc, dễ viết. Thực vậy, không còn những DB::table('table_name') lặp đi lặp lại bởi bởi eloquent đã implement ActiveRecord, mỗi model sẽ được mapping với một table cụ thể, và việc của ta chỉ là thao tác trên model đó.
 $product = Product::find($productId); //Eloquent
 $product = DB::table('product')->where('id', $productId)->get(); //Query Builder
  • tận dụng được các tính năng ưu việt như ActiveRecord pattern, relationships, Model boot, softDelete, eager loading. thao tác nhiều hơn với dữ liệu trả về (là collection chứ không phải 1 array như query builder)

Query Builder

  • thời gian execute nhanh hơn so với Eloquent. Mình đã tìm qua khá nhiều bài so sánh 2 thanh niên này, và tất cả đều đo được thời gian query của Eloquent là lâu hơn so với Query Builder.
  • linh hoạt hơn. Nên nhớ là trong query builder, ta hoàn toàn có thể chạy một raw query y như mysql, điều này sẽ rất tốt khi cần phải thực hiện các query phức tạp

Eager loading

Eager loading là 1 technique dùng để giải quyết bài toán N+1. Giả sử ta muốn load tất cả các products. Sau đó in ra tất cả từng category của từng product (giả sử mỗi product chỉ thuộc duy nhất 1 category). Ta làm như sau:

  $products = Product::all();
  for ($product : $products) {
      echo $product->category->name;
  }

Với N product, ta sẽ phải thực hiện tất cả N+1 query để get tất cả product và category tương ứng.

Ta sẽ giải quyết vấn đề này với eager loading. Load tất cả product và category tương ứng chỉ với 2 query. (lưu ý là phải set relationship category cho product trong file model trước)

  $products = Product::with('category')->get();

câu lệnh trên tương ứng với

  $productIds = Product::lists('id');
  $categories = Category::whereIn('id', $productIds)->get();

Accessors & Mutators

2 khái niệm này không chỉ có trong laravel mà thực chất còn được biết tới bởi nhiều language khác nữa. Accessor method trigger khi bạn get một field nào đó trong table. Ví dụ với model Product, ta muốn tất cả name của product khi lấy ra khỏi database đều được viết hoat chữ cái đầu tiên, ta sẽ set 1 accessor là getNameAttribute(value) trong đó value chính là product name được trả về. Giờ mỗi lần ta truy cập $$roduct->name, dữ liệu trả về sẽ được viết hoa chữ cái đầu tiên:

 class Product extends Model {
     protected $fillable = ['name', 'category_id', 'type', 'quality'];

     public function getNameAttribute($value) {
         return ucfirst($value)
     }
 }

Tương tự với Mutator, method này sẽ trigger khi ta set một field nào đó trong table. vẫn tiếp tục với model Product trên, ta muốn set value cho attribute type khi value của nó bằng null, ta sẽ tạo một function là setNameAttributes($$alue). Giờ mỗi lần thêm, hoặc update product type, nếu value null thì nó sẽ được set mặc định là "undefied".

 class Product extends Model {
     ...
     public function setNameAttribute($value) {
         if (!isset($value)) {
             $value = "undefined";
         }
     }
     ...
 }

Middleware

Middleware là phần nằm giữa client và server, xử lý các request vào ra server của bạn. Có một middleware đã được nhắ tới ơ trên là VerifyCsrfToken nhằm kiểm tra sự hợp lệ của 1 request post.

Defined Middleware ta có thể tự định nghĩa ra 1 middleware. ví dụ tôi muốn định nghĩa ra 1 middle 'admin', dùng để filter role của user, nếu là user thì cho qua, không thì chặn lại không proceed request. Ta làm như sau: trong folder middleware, tạo 1 class là Admin

php artisan make:middleware Admin

Tiếp đó, thêm phần xử lý vào method handle

class Admin
{
    protected $auth;

    public function __construct(Guard $auth)
    {
        $this->auth = $auth;
    }
    /**
     * Handle an incoming request.
     *
     * @param  IlluminateHttpRequest  $request
     * @param  Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        if ($this->auth->user()->isAdmin()) {
            return $next($request);
        }

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

Tiếp đó, đăng kí middleware này với laravel. Trong file kernel.php, ta thêm: 'admin' => AppHttpMiddlewareAdmin::class,

    protected $routeMiddleware = [
        'auth' => AppHttpMiddlewareAuthenticate::class,
        'auth.basic' => IlluminateAuthMiddlewareAuthenticateWithBasicAuth::class,
        'can' => IlluminateFoundationHttpMiddlewareAuthorize::class,
        'guest' => AppHttpMiddlewareRedirectIfAuthenticated::class,
        'throttle' => IlluminateRoutingMiddlewareThrottleRequests::class,
        'admin' => AppHttpMiddlewareAdmin::class,
    ];

Done             </div>
            
            <div class=

0