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.
Ứ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 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
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
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');
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
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!