--- tags: laravel --- # Session 工作階段 ![](https://i.imgur.com/a5sZ2et.jpg) ## 簡介 由於 HTTP 驅動的應用是無狀態的,Session 提供了一種在多個請求之間儲存有關用戶資料的方法。使用者資料一般被放在持久保存或後端以便供後續的請求取用 Laravel 通過同一個可讀性強的 API 處理各種自帶的後台驅動程序以支持諸如比較熱門的 Memcached, Redis 和資料庫 ### 設定 Session 的設定檔案儲存在 config/session.php 檔案中。請務必查看此檔案,看看有哪些可能可用的選項。預設情況下,Laravel 為絕大多數應用配置的 Session 驅動為 file ,並在大多數應用裏頭都運作良好。在生產環境中,你可以考慮使用 memcached 或 redis 驅動,讓 Session 的性能更加出色。假如你的應用透過多個網頁伺服器來進行負載平衡,你需要選擇一個作為核心來進行儲存已讓其他服務器可以存取,諸如 Redis 或 資料庫 這個 Session 驅動設定選項定義了每一個請求的 Session 資料將要存在哪裡,Laravel內建了很多好用的驅動給你選擇 |驅動| 說明| |---|---| |file| 將 Sessions 存在本地端的 storage/framework/sessions 資料夾| |cookie| Sessions 被存在安全且加密的 Cookies| |database| Sessions 被存在關聯資料庫| |memcached / redis| Sessions 被存在幾乎是最快的,透過快取來儲存| |dynamodb| Sessions 被存在 AWS DynamoDB| |array| Sessions 被存在 PHP 陣列且無法持久保存| > array 驅動主要是用來進行測試,以避免資料存在Session之後會被持久保存 ### 驅動程式先決條件 #### 資料庫 使用 database 作為 Session 驅動時,你需要創建一張包含 Session 各個數據的表格。以下是使用 Schema 建表的例子: ``` Schema::create('sessions', function ($table) { $table->string('id')->primary(); $table->foreignId('user_id')->nullable()->index(); $table->string('ip_address', 45)->nullable(); $table->text('user_agent')->nullable(); $table->text('payload'); $table->integer('last_activity')->index(); }); ``` 你可以使用 Artisan 命令 session:table 來生成此遷移檔案。如需了解更多的資料庫遷移檔案,你可以查看[Database Migration](/rC-AJDK4SOaqMlGPa1LXuA): ``` php artisan session:table php artisan migrate ``` #### Redis Laravel 在使用 Redis 作為 Session 驅動之前,需要安裝 Redis 的 PHP 擴展或者通過 Composer 安裝 predis/predis 擴展包 (~1.0)。Redis 的詳細設定資訊參考 Laravel Redis 文件 >技巧: > >在 session 配置檔案中,connection 選項可用於指定會話使用哪個 Redis 連接 ## 使用 Session ### 取用資料 Laravel 中處理 Session 資料有兩種主要方法:全局幫助函式 session() 和通過一個 Request 實例。首先,我們來看看通過控制器方法型別提示一個 Request 實例來訪問 Session。控制器方法依賴項會透過 Laravel 服務容器來實現自動注入 ``` <?php namespace App\Http\Controllers; use App\Http\Controllers\Controller; use Illuminate\Http\Request; 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'); // } } ``` 當你從 Session 獲取值時,你還可以傳遞一個預設值作為 get() 的第二個參數。如果 Session 中不存在指定的鍵,便會回傳這個預設值。若傳遞一個 Closure 作為 get() 方法的預設值,並且所請求的鍵並不存在時, get() 將執行 Closure 並回傳其結果: ``` $value = $request->session()->get('key', 'default'); $value = $request->session()->get('key', function () { return 'default'; }); ``` #### 全域 Session 幫助函式 你也可以使用全域的 PHP 幫助函式 session() 來獲取和儲存 Session 資料。 使用單個字串類型的值作為參數呼叫幫助函式 session() 時,它會回傳該字串對應的 Session 鍵的值。當使用一個鍵值對陣列作為參數呼叫幫助函式 session() 時,傳入的鍵值將會儲存在 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']); }); ``` > 技巧: > > 通過 HTTP 請求實例操作 Session 與使用全域幫助函式 session() 兩者之間並沒有實質上的區別。這兩種方法都可以通過所有測試用例中可用的 assertSessionHas() 來進行測試 #### 取得所有 Session 資料 如果你想獲取 session 中的所有資料,可以使用 all() `$data = $request->session()->all();` #### 確認是否某個值存在於 Session 要確定 Session 中是否存在某個值,可以使用 has()。如果該值存在且不為 null,那麼 has() 會回傳 true: ``` if ($request->session()->has('users')) { // } ``` 要確定 Session 中是否存在某個值,即使其值為 null,也可以使用 exists() 。如果值存在,則 exists() 返回 true: ``` if ($request->session()->exists('users')) { // } ``` ### 儲存資料 想要儲存資料到 Session,你可以使用 put() ,或者使用幫助函式 session() ``` // Via a request instance... $request->session()->put('key', 'value'); // Via the global "session" helper... session(['key' => 'value']); ``` #### 保存資料到 Session 陣列中 push() 可以將一個新的值添加到 Session 陣列內。例如假設 user.teams 這個鍵是包括團隊名稱的陣列,你可以這樣將一個新的值加入到陣列中: `$request->session()->push('user.teams', 'developers');` #### 取得並刪除一筆資料 pull() 可以只使用一條語句就從 Session 中取用資料並刪除該資料: `$value = $request->session()->pull('key', 'default');` #### 增加或減少 Session 裏頭的值 假如你的 Session 資料包含一個整數而你希望能進行增減,你能使用 increment() 和 decrement() ``` $request->session()->increment('count'); $request->session()->increment('count', $incrementBy = 2); $request->session()->decrement('count'); $request->session()->decrement('count', $decrementBy = 2); ``` ### 閃存資料 有時候你可能想在 Session 中保存數據用於下一次請求,這時你可以使用 flash()。使用這個方法保存在 Session 中的資料,只會保留到下一個 HTTP 請求,然後就會被刪除。閃存資料主要用於短期的狀態訊息: `$request->session()->flash('status', 'Task was successful!');` 如果你需要在更多的請求中使用到該一次性資料,你可以使用 reflash() ,該方法會將所有一次性請求保留到下一次請求。如果你想保存特定的一次性數據,你可以改用 keep() : ``` $request->session()->reflash(); $request->session()->keep(['username', 'email']); ``` 如為了只針對當前請求的一次性資料進行延長,你可改用 now() `$request->session()->now('status', 'Task was successful!');` ### 刪除資料 forget() 會從 Session 中刪除指定資料,如果想從 Session 中刪除所有資料,可以使用 flush () ``` // Forget a single key... $request->session()->forget('name'); // Forget multiple keys... $request->session()->forget(['name', 'status']); $request->session()->flush(); ``` ### 重新生成 Session ID 重新生成 session ID 通常是為了防止惡意用戶利用"Session 固定"對你的應用進行攻擊 如果你使用的是內建的 LoginController,Laravel 會自動重新生成身份認證中的 Session ID。否則,你需要手動呼叫 regenerate() 重新生成 Session ID `$request->session()->regenerate();` 假如你需要重新生成 Session ID 並同時從Session中移除所有的資料,而且以單一命令來完成,可使用 invalidate() `$request->session()->invalidate();` ## Session 堵塞 > 注意: > > 要利用 Session 堵塞,您的應用必須使用支持 atomic locks 的快取驅動。 當前這些快取驅動包括 memcached,dynamodb,redis 和 database 驅動。 另外也不能使用 cookie 驅動 預設情況下,Laravel 允許使用同一 Session 的請求同時執行。 例如如果你使用 JavaScript HTTP 庫向應用發出兩個 HTTP 請求,它們將同時執行。 對於許多應用來說,這不是問題。 但是在一小部分應用中可能會丟失 Session 資料,這些應用會向兩個不同的應用路由同時發出請求,這兩個路由都將資料寫入 Session 為了處理這種情況,Laravel 允許您限制指定 Session 的並發請求。 首先你可以簡單地在定義路由時呼叫 block()。 在這個例子中,進入到 /profile 路由的請求將添加 Session 鎖。 在 Session 鎖住期間,/ profile 或 / order 路由共享相同 session ID 的任何請求都將等待第一個請求執行完成,然後再繼續執行