Laravel onetime url use signed route

  • January 16, 2021
  • 1192

Yêu cầu đặt ra là tạo một url chỉ sử dụng được trong 1 khoảng thời gian xác định hoặc chỉ cho phép sử dụng 1 lần. Để giải quyết yêu cầu này mình sẽ sử dụng laravel signed url. Laravel cho phép bạn dễ dàng tạo signed URL thông qua router name. Khi tạo các url này có một signature hash được tạo ra thêm query string cho phép Laravel xác thực URL này không bị sửa đổi kể từ khi nó được tạo. Sử dụng Signed url giúp hạn chế request giả mạo lên server.

Laravel signed url

Create signed url đơn giản

Ứng dụng của bạn sử dụng url {domain}/articles/1/unsubscribe để User bỏ theo dõi một bài viết, với url trên thì User có thể sử đổi $postId từ 1 thành bất kì spostId bài viết khác, lúc này cần sử dụng Signed routes để hạn chế các request lên server từ các URL giả mạo này.

Route thông thường khi bạn sử dụng laravel

Route bỏ theo dõi bài viết thông thường


Route::get('articles/{$postId}/unsubscribe', function($postId) {
  //Logic code
})->name('articles.unsubscribe');

Generate url thông thường sử dụng URL facade


use Illuminate\Support\Facades\URL;

Url::route('articles.unsubscribe', ['postId' => '1']);
// output
{domain}/articles/1/unsubscribe

Sử dụng signed url

Route bỏ theo dõi bài viết sử dụng signed url, để tạo 1 signed url laravel cung cấp hàm signedRoute đối số thứ 1 là route name, đối số thứ 2 là các query string truyền lên url, lúc này url sẽ thêm 1 query string là signature = signature hash, laravel sẽ xác thực url này dựa vào signature hash này.


Url::signedRoute('articles.unsubscribe', ['postId' => '1']);
// output
{domain}/articles/1/unsubscribe?signature=94449dc03df2007febcb7b7e0d56a03d1a7babebbf9b64c603c118e53917a1c8

Validate signed route request

Có 2 cách để validate signed route
Sử dụng hasValidSignature method với Request


use Illuminate\Http\Request;

Route::get('articles/{$postId}/unsubscribe', function (Request $request) {
    if (! $request->hasValidSignature()) {
        abort(401);
    }

    // ...
})->name('unsubscribe');

Sử dụng middleware


Route::get('articles/{$postId}/unsubscribe', function($postId) {
  //Logic code
})->name('articles.unsubscribe')->middleware('signed');

Create signed url chỉ dùng được trong 1 thời gian xác định

Dể tạo 1 signed url chỉ sử dụng được trong một thời gian xác định, laravel cung cấp hàm temporarySignedRoute đối số thứ 1 là route name, đối số thứ 2 là thời url có thể thử dụng sau khi created, đối số thứ 3 là các query string truyền trên url. So với cách trước thì url có thêm 1 query string là expires để xác thực thời gian sống của url.


Url:: temporarySignedRoute('articles.unsubscribe', now()->addMinutes(30), ['postId' => '1']);
// output, url này chỉ sử dụng được trong vòng 30 phút
{domain}/articles/1/unsubscribe?expires=1610787048&signature=94449dc03df2007febcb7b7e0d56a03d1a7babebbf9b64c603c118e53917a1c8

Create signed url chỉ dùng được 1 lần

Laravel không support tạo signed url chỉ sử dụng 1 lần, nhưng mình có thể sử dụng signed url có thời gian expried và cache để thực hiện phần này, do mỗi một signed url sẽ sinh ra một signature hash, nên mình sẽ lưu một cache key là signature hash có thời gian tồn tại lớn hơn thời gian expried của url, value là số lần mà url được request, check nếu tồn tại cache key, và get cache key >= 1 thì trả về trang 403. Với cách này mình có thể tạo ra url có thể sử dụng 1, 2 ... n lần tuỳ vào từng yêu cầu

Như vậy là chúng ta đã xử lí được yêu cầu tạo ra một url có thể sử dụng trong một thời gian xác định, và url có số lần request xác định. Hi vọng bài viết sẽ giúp ích cho các bạn!