Laravel: Session

Mục lục bài viết:

Giới thiệu

Vì các ứng dụng điểu khiển HTTP là không chính thống, nên session sẽ cung cấp một cách lưu trữ thông tin của người dùng thông qua đa yêu cầu. Laravel với một loạt các session backend được truy cập thông qua một API sinh động và thống nhất. Hỗ trợ cho những backend phổ biến như Memcached, Redis và các cơ sở dữ liệu được tích hợp bên ngoài.

Cấu hình

Tập tin cấu hình session được lưu trữ tại config/session.php. Bạn có thể mở để xem các tùy chọn được dùng trong file này. Mặc định thì Laravel được cấu hình để sử dụng trình điều khiển session có tên file, nó sẽ làm việc tốt trên nhiều ứng dụng. Trong các ứng dụng thực tế bạn có thể sử dụng các trình điều khiển memcached hoặc redis để tăng hiệu năng của session.

Tùy chọn cấu hình session driver sẽ định nghĩa nơi lưu trữ dữ liệu session cho mỗi yêu cầu. Laravel có một số trình điểu khiển lớn như sau:

  • file – các session được lưu trữ trong storage/framework/sessions.
  • cookie – các session được lưu trữ trong các cookie bảo mật và mã hóa.
  • database – các session được lưu trữ trong cơ sở dữ liệu quan hệ.
  • memcached / redis – các session được lưu trữ tại một trong các trình điểu khiển này sẽ nhanh và được đệm dựa trên các bộ lưu trữ cơ sở.
  • array – session được lưu trữ trong một mảng PHP và sẽ không được tồn tại lâu bền.

 

Trình điều khiển mảng được dùng trong quá trình kiểm thử và ngăn chặn dữ liệu được lưu trữ vào session.

Điều kiện tiên quyết về trình điều khiển

Database

Khi sử dụng trình điều khiển session database thì bạn sẽ cần tạo một bảng để chứa các thành phần của session. Dưới đây là một ví dụ khai báo Schema cho bảng:

Schema

::

create

(

'sessions'

,

function

(

$table

)

{

$table

-

>

string

(

'id'

)

-

>

unique

(

)

;

$table

-

>

integer

(

'user_id'

)

-

>

nullable

(

)

;

$table

-

>

string

(

'ip_address'

,

45

)

-

>

nullable

(

)

;

$table

-

>

text

(

'user_agent'

)

-

>

nullable

(

)

;

$table

-

>

text

(

'payload'

)

;

$table

-

>

integer

(

'last_activity'

)

;

}

);

Bạn có thể sử dụng câu lệnh Artisan session:table để tạo migration này:

php artisan session

:

table php artisan migrate

Redis

Trước khi sử dụng session Redis với Laravel bạn sẽ cần phải cài đặt PhpRedis bằng PECL hoặc cài đặt gói predis/predis (~1.0) bằng Composer. Bạn có thể cấu hình các kết nối Redis trong tập tin cấu hình database. Trong tập tin cấu hình session thì tùy chọn connection được dùng để xác định xem kết nối Redis nào được dùng bởi session.

Sử dụng session

Truy xuất dữ liệu

Có hai cách chủ yếu để làm việc với dư liệu session trong Laravel là helper session và thông qua một thể hiện Request. Trước tiên ta sẽ tìm hiểu cách truy cập session với thể hiện Request, thể hiện này có thể được hiển thị tại phần gợi ý khi viết code tại phương thức controller. Có một điều cần lưu ý là các dependencies phương thức controller được tự động bơm vào thông qua bộ chứa dịch vụ Laravel:

<?php

namespace

App

\

Http

\

Controllers

;

use

Illuminate

\

Http

\

Request

;

use

App

\

Http

\

Controllers

\

Controller

;

class

UserController

extends

Controller

{

/** * Show the profile for the given user. * * @param Request $request * @param int $id * @return Response */

public

function

show

(

Request

$request

,

$id

)

{

$value

=

$request

-

>

session

(

)

-

>

get

(

'key'

)

;

//

}

}

Khi bạn truy xuất một mục từ session, bạn cũng có thể chuyển một giá trị mặc định làm đối số thứ hai cho phương thức get. Giá trị mặc định này sẽ được trả về nếu khóa được chỉ định không tồn tại trong session. Nếu bạn truyền một Closure làm giá trị mặc định cho phương thức get và khóa được yêu cầu không tồn tại, thì Closure sẽ được thực thi và trả về kết quả:

$value

=

$request

->

session

()->

get

(

'key'

,

'default'

);

$value

=

$request

->

session

()->

get

(

'key'

,

function

() {

return

'default'

; });

Trình trợ giúp session global

Bạn cũng có thể sử dụng hàm PHP session global để truy xuất và lưu trữ dữ liệu trong session. Khi trình trợ giúp session được gọi với một đối số chuỗi, đơn, thì nó sẽ trả về giá trị của khóa session đó. Khi trình trợ giúp được gọi với một mảng các cặp key/value, thì những giá trị đó sẽ được lưu trữ trong session:

Route::

get

(

'home'

,

function

() {

// Retrieve a piece of data from the session...

$value

=

session

(

'key'

);

// Specifying a default value...

$value

=

session

(

'key'

,

'default'

);

// Store a piece of data in the session...

session

([

'key'

=>

'value'

]); });

Có một chút khác biệt thực tế giữa việc sử dụng session thông qua phiên bản yêu cầu HTTP so với việc sử dụng trình trợ giúp session global. Cả hai phương pháp đều có thể kiểm tra được thông qua phương thức assertSessionHas có sẵn trong tất cả các trường hợp thử nghiệm của bạn.

Truy xuất tất cả dữ liệu session

Nếu bạn muốn truy xuất tất cả dữ liệu session thì bạn có thể sử dụng phương thức all:

$data

=

$request

->

session

()->

all

();

Kiểm tra session có tồn tại

Để xác định xem một session nào đó có tồn tại hay không, bạn có thể sử dụng phương has. Phương thức này trả về true nếu có và ngược lại là null:

if

(

$request

->

session

()->

has

(

'users'

)) {

//

}

Để xác định xem một session tồn tại hay không, ngay cả khi giá trị của nó là null, bạn có thể sử dụng phương exists. Phương thức exists trả về true nếu session đó tồn tại:

if

(

$request

->

session

()->

exists

(

'users'

)) {

//

}

Lưu trữ dữ liệu

Để lưu trữ dữ liệu trong session, bạn thường sẽ sử dụng phương thức put hoặc trình trợ giúp session:

// Thông qua 1 thể hiện của Request

$request

->

session

()->

put

(

'key'

,

'value'

);

// Thông qua helper session

session

([

'key'

=>

'value'

]);

Đẩy vào các giá trị session mảng

Các phương thức push có thể được sử dụng để đẩy một giá trị mới vào một giá trị session đó là một mảng. Ví dụ: nếu khóa user.teams chứa một mảng tên nhóm, bạn có thể đẩy một giá trị mới vào mảng như sau:

$request

->

session

()->

push

(

'user.teams'

,

'developers'

);

Lấy và xóa một mục

Phương thức pull sẽ lấy và xóa một mục từ session bằng lệnh sau:

$value

=

$request

->

session

()->

pull

(

'key'

,

'default'

);

Dữ liệu Flash

Đôi khi bạn có thể muốn lưu trữ các mục trong phiên chỉ cho yêu cầu tiếp theo. Bạn có thể làm như vậy bằng cách sử dụng phương thức flash. Dữ liệu được lưu trữ trong phiên sử dụng phương thức này sẽ có sẵn ngay lập tức và trong yêu cầu HTTP tiếp theo. Sau yêu cầu HTTP tiếp theo, dữ liệu đã flash sẽ bị xóa. Dữ liệu flash chủ yếu hữu ích cho các thông báo trạng thái tồn tại trong thời gian ngắn:

$request

->

session

()->

flash

(

'status'

,

'Task was successful!'

);

Nếu bạn cần giữ dữ liệu flash của mình cho một số yêu cầu, bạn có thể sử dụng phương thức reflash, phương thức này sẽ giữ tất cả dữ liệu flash cho một yêu cầu bổ sung. Nếu bạn chỉ cần giữ dữ liệu flash cụ thể, bạn có thể sử dụng phương thức keep:

$request

->

session

()->

reflash

();

$request

->

session

()->

keep

([

'username'

,

'email'

]);

Xóa dữ liệu

Phương thức forget sẽ loại bỏ một phần dữ liệu trong phiên làm việc. Nếu bạn muốn xóa tất cả dữ liệu khỏi phiên, bạn có thể sử dụng phương thức flush:

// Xóa 1 session cụ thể...

$request

->

session

()->

forget

(

'key'

);

// Xóa nhiều session...

$request

->

session

()->

forget

([

'key1'

,

'key2'

]);

// Xóa tất cả các session...

$request

->

session

()->

flush

();

Tạo lại ID phiên

Việc tạo lại ID session thường được thực hiện để ngăn người dùng độc hại khai thác cuộc tấn công sửa session trên ứng dụng của bạn.

Laravel tự động tạo lại ID phiên trong quá trình xác thực nếu bạn đang sử dụng LoginController; tuy nhiên, nếu bạn cần tạo lại ID session theo cách thủ công, bạn có thể sử dụng phương thức regenerate.

$request

->

session

()->

regenerate

();

Chặn session

Để sử dụng tính năng chặn session, ứng dụng của bạn phải sử dụng trình điều khiển cache hỗ trợ khóa nguyên tử (atomic lock). Hiện nay, những trình điều khiển bộ nhớ cache bao gồm memcacheddynamodbredis, và database. Ngoài ra, bạn không thể sử dụng trình điều khiển session cookie.

Theo mặc định, Laravel cho phép các yêu cầu sử dụng cùng một phiên thực thi đồng thời. Vì vậy, ví dụ: nếu bạn sử dụng thư viện JavaScript HTTP để thực hiện hai yêu cầu HTTP tới ứng dụng của mình, cả hai sẽ thực thi cùng một lúc. Đối với nhiều ứng dụng, đây không phải là vấn đề; tuy nhiên, việc mất dữ liệu phiên có thể xảy ra trong một nhóm nhỏ các ứng dụng thực hiện các yêu cầu đồng thời tới hai điểm cuối ứng dụng khác nhau, cả hai đều ghi dữ liệu vào session.

Để giảm thiểu điều này, Laravel cung cấp chức năng cho phép bạn giới hạn các yêu cầu đồng thời cho một session nhất định. Để bắt đầu, bạn có thể chỉ cần thêm phương thức block vào định nghĩa route của mình. Trong ví dụ này, một yêu cầu đến endpoint /profile sẽ nhận được một khóa session. Trong khi khóa này đang được giữ, mọi yêu cầu đến các endpoint /profile hoặc /order sẽ cùng chia sẻ một ID session sẽ đợi yêu cầu đầu tiên kết thúc thực thi trước khi tiếp tục thực hiện:

Route::

post

(

'/profile'

,

function

() {

//

})->

block

(

$lockSeconds

=

10

,

$waitSeconds

=

10

) Route::

post

(

'/order'

,

function

() {

//

})->

block

(

$lockSeconds

=

10

,

$waitSeconds

=

10

)

Phương thức block chấp nhận hai đối số tùy chọn. Đối số đầu tiên được phương thức block chấp nhận là số giây (second) tối đa mà khóa session sẽ được giữ trước khi nó được giải phóng. Tất nhiên, nếu yêu cầu kết thúc thực hiện trước thời gian này, khóa sẽ được giải phóng sớm hơn.

Đối số thứ hai được phương thức block chấp nhận đó là số giây yêu cầu phải đợi trong khi cố gắng lấy khóa session. Một Illuminate\Contracts\Cache\LockTimoutException sẽ được ném ra nếu yêu cầu không thể đạt được khóa session với số giây nhất định.

Nếu cả hai đối số này đều không được thông qua, khóa sẽ được nhận trong tối đa 10 giây và các yêu cầu sẽ đợi tối đa 10 giây trong khi cố gắng lấy khóa:

Route::

post

(

'/profile'

,

function

() {

//

})->

block

()

Thêm trình điều khiển phiên tùy chỉnh

Triển khai Trình điều khiển

Trình điều khiển phiên tùy chỉnh của bạn nên triển khai SessionHandlerInterface. Giao diện này chỉ chứa một số phương thức đơn giản mà chúng ta cần thực hiện. Một triển khai MongoDB sơ khai trông giống như sau:

<?php

namespace

App\Extensions;

class

MongoSessionHandler

implements

\

SessionHandlerInterface {

public

function

open

(

$savePath

,

$sessionName

) {}

public

function

close

() {}

public

function

read

(

$sessionId

) {}

public

function

write

(

$sessionId

,

$data

) {}

public

function

destroy

(

$sessionId

) {}

public

function

gc

(

$lifetime

) {} }

Laravel không gửi kèm thư mục để chứa các tiện ích mở rộng của bạn. Bạn có thể tự do đặt chúng ở bất cứ đâu bạn thích. Trong ví dụ này, chúng tôi đã tạo một thư mục Extensions để chứa MongoSessionHandler.

Ta hãy tìm hiểu nhanh về các phương thức trên nhé:

  • Phương thức open thường sẽ được sử dụng trong tập tin dựa trên hệ thống session store. Vì Laravel đi kèm với trình điều khiển session file, nên bạn sẽ hầu như không cần đặt bất cứ thứ gì vào phương thức này. Thực tế là do thiết kế giao diện kém (mà chúng ta sẽ thảo luận ở phần sau) mà PHP yêu cầu chúng ta thực hiện phương thức này.
  • Phương thức close, giống như phương thức open, có thể cũng thường được bỏ qua. Đối với hầu hết các trình điều khiển, nó không cần thiết.
  • Phương thức read nên trả về chuỗi phiên bản của dữ liệu session liên quan đến việc $sessionId đã cho. Không cần thực hiện bất kỳ điều gì khi truy xuất hoặc lưu trữ dữ liệu session trong trình điều khiển của bạn, vì Laravel sẽ thực hiện tuần tự hóa cho bạn.
  • Phương thức write nên viết cho chuỗi $data gắn liền với $sessionId một số hệ thống lưu trữ liên tục, chẳng hạn như MongoDB, Dynamo,… Một lần nữa, bạn không nên thực hiện bất kỳ điều gì – Laravel sẽ đã xử lý điều đó cho bạn.
  • Phương thức destroy nên loại bỏ các dữ liệu liên quan đến $sessionId trong việc lưu trữ liên tục.
  • Phương thức gc nên hủy toàn bộ dữ liệu phiên cũ hơn $lifetime đã cho, đó là một timestamp UNIX. Đối với các hệ thống tự hết hạn như Memcached và Redis, phương thức này có thể để trống.

Đăng ký trình điều khiển

Một khi trình điều khiển của bạn được triển khai thì bạn có thể đăng ký nó với framework. Để thêm các trình điều khiển bổ sung vào chương trình phụ trợ session của Laravel, bạn có thể sử dụng phương thức extend trên Session facade. Bạn nên gọi phương thức extend từ phương thức boot của service provider. Bạn có thể thực hiện việc này từ AppServiceProvider hoặc tạo một provider mới:

<?php

namespace

App\Providers;

use

App\Extensions\MongoSessionHandler;

use

Illuminate\Support\Facades\Session;

use

Illuminate\Support\ServiceProvider;

class

SessionServiceProvider

extends

ServiceProvider

{

/** * Register any application services. * * @return void */

public

function

register

() {

//

}

/** * Bootstrap any application services. * * @return void */

public

function

boot

() { Session::

extend

(

'mongo'

,

function

(

$app

) {

// Return implementation of SessionHandlerInterface...

return

new

MongoSessionHandler

; }); } }

Sau khi trình điều khiển phiên đã được đăng ký, bạn có thể sử dụng trình điều khiển mongo trong tệp cấu hình config/session.php.