簡介
laravel 使實施認證的變得非常簡單,事實上,它提供了非常全面的配置項以適應應用的業務。認證的設定檔存放在 config/auth.php 目錄,這裡的每個選項都提供了完善的注釋文檔,你可以從這裡調整認證服務的行為。
在 laravel 中,認證服務的核心是由 guards(守衛) 和 providers(供應商) 組成。守衛定義了從請求中驗證使用者的方式,比如說,laravel 內建了 session 守衛和 token 守衛,session 守衛是從所儲存的會話及 cookies 中去認證請求中的使用者的,而 token 守衛則是從請求中的 API token 進行使用者認證的。
供應商則定義了從持久化儲存中擷取使用者的方式。laravel 自身提供了 Eloquent 和 database 查詢構造器兩種檢索方式。當然,你也可以添加額外的供應商去做這些。
如果這聽起來有些混亂,請不要擔心。大多數的應用程式是根本不需要修改認證服務的預設配置資訊的。
資料庫注意事項
預設的,laravel 在 app 目錄下包含了 App\User Eloquent 模型。該模型使用了預設的 Eloquent 認證驅動。如果你的應用不是使用 Eloquent 驅動,你可以使用 database 認證驅動,database 認證驅動是基於 laravel 的查詢構造器的。
當你為 App\User 模型去構建資料庫表結構時,你應該確認密碼應該保持最少 60 個字元的長度,預設的 255 是一個比較好的選擇。
另外,你需要確保 users 表中包含了一個可以為 null 的字串列 remember_token,它應該具有 100 個字元的長度。這個欄位是用來儲存使用者保持長期登入的 token 值的。你可以在遷移中使用 $table->rememberToken() 來快速的添加該列。
快速開始
laravel 裝載了兩個用來進行認證的控制器,它們儲存在 App\Http\Controllers\Auth 命名空間下。AuthController 用來處理使用者註冊及認證的邏輯,而 PasswordController 包含了使用者找回密碼的相關邏輯。它們每個控制器都是通過引入 trait 來包含他們所需要的方法。對於大多數應用來說,你是完全沒有必要去修改這些控制器的。
路由
laravel 提供了一個快速的方法來產生認證的路由和視圖的腳手架,你可以使用 artisan 命令:
php artisan make:auth
在一個新的應用中,這個命令會用來進行安裝註冊和登入的視圖,也會注入所有的認證相關的路由。HomeController 也會被產生。這個控制器提供了 post 登入請求的處理方法。你也可以根據自己的需求刪除或修改這個控制器。
視圖
就如上面所提到的,php artisan make:auth 命令會建立所有認證相關的視圖,並且儲存在 resources/views/auth 目錄下。
make:auth 命令也會建立 resoucres/views/layouts 目錄,並在該目錄下為應用建立了一個基本的布局。所有的這些視圖都是使用了 Bootstrap CSS 架構,你可以根據自身的需求去定製化修改。
進行認證
現在你已經具有了認證相關的控制器、路由和視圖,你的應用已經具備了註冊和認證的能力。你可以通過瀏覽器訪問你的應用,認證控制器已經包含了所有的認證使用者和儲存使用者到資料庫的方法(通過 traits)。
自訂重新導向地址
當使用者通過認證後會被重新導向到 / URL。你可以通過在 AuthController 控制器中定義 redirectTo 屬性來修改重新導向地址:
protected $rediredtTo = '/home';
當使用者沒有認證成功,它會重新導向回登入地址。
自訂守衛
你也可以定製化 guard 用來驗證使用者。你需要在 AuthController 中定義 guard 屬性。它的值應該是與你的 auth.php 配置中的某一個 guards 值相匹配的:
protected $guard = 'admin';
自訂 驗證/儲存
你可能會想要在使用者註冊時儲存一些其他必要的表單欄位,進而將新增使用者的資訊儲存到資料庫中,這個時候你就需要修改 AuthController 類了,這個類主管使用者的建立和表單的驗證。
在 AuthController 類中使用 validate 方法來驗證驗證表單與驗證規則的匹配程度。你可以根據自身的需要來修改這個方法。
在 AuthController 類中使用 create 方法用來在資料庫中新增一條使用者的記錄。這裡使用了 Eloquent ORM。你可以根據自身的需求修改這個方法。
擷取已認證的使用者
你可以通過 Auth 假面來訪問已經認證的使用者:
$user = Auth::user();
另外,一旦使用者經過驗證,你可以通過 Illuminate\Http\Request 的執行個體來訪問經過認證後的使用者。你應該記得,類型提示的類會被自動的注入到你的控制器方法中:
user()) { // $request->user() returns an instance of the authenticated user... } }}
判斷目前使用者是否已被認證
你可以通過 Auth 假面的 check 方法來判斷目前使用者是否已經被認證。如果該使用者已經被認證則會返回 true:
if (Auth::check()) { // The user is logged in...}
你應該使用中介軟體來過濾未被認證的使用者訪問某些路由或控制器。
保護路由
路由中介軟體可以被用來限制只有已經被認證的使用者才能訪問所給定的路由。laravel 內建了 auth 中介軟體,該中介軟體被定義在 app\Http\Middleware\Authenticate.php 檔案中。你只需要在定義路由時附加上該中介軟體就可以了:
// Using A Route Closure...Route::get('profile', ['middleware' => 'auth', function () { // Only authenticated users may enter... }]);// Using A Controller...Route::get('profile', [ 'middleware' => 'auth', 'uses' => 'ProfileController@show']);
當然,如果你使用控制器來註冊路由,你可以在控制器的建構函式中使用 middleware 方法來附加中介軟體:
public function __construct(){ $this->middleware('auth');}
指定守衛
當你附加 auth 中介軟體到路由時,你也可以指定選擇哪個守衛去提供認證:
Route::get('profile', [ 'middleware' => 'auth:api', 'uses' => 'ProfileController@show']);
所指定的守衛應該與設定檔 auth.php 中的 guards 數組鍵之一相匹配。
認證節流
如果你使用了 laravel 內建的 AuthController 類,那麼 Illuminate\Foundation\Auth\ThrottlesLogins trait 可以被用來限制使用者嘗試登陸的次數。預設的,當使用者進行登陸數次失敗時,其將會在一分鐘內無法進行登陸。節流是根據使用者的 username / e-mail 和 IP 位址來判定的:
手動進行使用者認證
當然,你沒有必要一定使用 laravel 內建的認證控制器。如果你選擇刪除這些認證控制器,那麼你需要直接的使用 laravel 認證類來系統管理使用者的認證。別擔心,這當然不在話下。
我們將通過 Auth 假面來訪問 laravel 的認證服務,所以,我們要確保在類檔案的頂部引入 Auth 假面。接著,讓我們查看一下 attempt 方法:
$email, 'password' => $password])) { // Authentication passed... return redirect()->intended('dashboard'); } }} attempt 方法接收一個索引值對數組來作為第一個參數。數組中的值用來在資料庫中尋找相匹配的使用者。所以,在上面的例子中,會返回匹配到 email 列為 $email 的使用者。如果使用者被找到,儲存在資料庫中的雜湊後的密碼會和數組中經過雜湊加密的 password 值進行匹配。如果兩個雜湊後的值匹配成功的話,就會開啟一個該使用者已經認證的會話。
如果認證成功,則 attempt 方法會返回 true。否則返回 false。
intended 方法用來返回給重新導向器使用者在登入前所想要前往的 URL 地址,該方法也接收一個參數作為所請求地址不可用時的備用地址。
指定額外的認證資訊
如果你需要,你也可以增加一些額外條件做認證查詢,比如,你需要驗證被標記為 'active' 的使用者:
if (Auht::attempt(['email' => $email, 'password' => $password, 'active' => 1])) { // The user is active, not suspended, and exists.}
注意,在上面的例子中 email 並不是必備的選項,這僅僅只是作為一個樣本。你可以使用任何進行使用者認證的憑證來映射資料庫中的欄位。
訪問指定的守衛執行個體
你可以使用 Auth 假面的 guard 方法來擷取你所需要的守衛執行個體。這使你可以在同一個應用中管理多種認證模型或使用者表,並實現獨立的認證。
guard 方法中所傳遞的守衛名稱應該與設定檔 auth.php 中 guards 之一相匹配:
if (Auth::guard('admin')->attempt($credentials)) { //} 登出
你可以使用 Auth 假面的 logout 方法來進行使用者的退出,該方法將清除使用者認證的會話資訊:
Auth::logout();
記住使用者
如果你想要在你的應用中提供 記住我 的功能,你只需要在 attempt 方法中傳遞一個布爾值作為第二個參數,這將保持使用者的認證資訊直到使用者手動的退出登入。當然,這要求你的使用者表中必須包含 remember_token 列:
if (Auth::attempt(['email' => $email, 'password' => $password], $remember)) { // The user is being remembered...} 如果你作為被記住的使用者登入的,那麼你可以使用 viaRemember 方法來判斷使用者的認證是不是通過 記住我 的 cookie:
if (Auth::viaRemember()) { //} 其它認證方法
通過使用者執行個體進行認證
如果你需要在應用中記錄一個已經存在的使用者執行個體,你可以使用 login 方法。所給定的參數必須是實現了 Illuminate\Contracts\Auth\Authenticatable 契約的一個執行個體。當然,在 laravel 中 App\User 模型已經實現了這個介面:
Auth::login($user);
當然,你也可以指定守衛執行個體:
Auth::guard('admin')->login($user); 通過使用者ID直接認證
你可以使用 loginUseingId 方法來通過 ID 記錄使用者的資訊,該方法簡單的接收一個需要進行認證的使用者的主鍵:
Auth::loginUsingId(1);
僅認證使用者一次
once 方法只在當前請求中進行使用者認證。不會儲存會話或 cookies。這對構建無狀態的 API 很有協助。once 方法和 attempt 具有相同的簽證方式:
if (Auth::once($credentials)) { //} 基於 HTTP 的認證
基於 HTTP 的認證提供了快速的使用者認證機制而不用設立專門的登入頁面。為了開始,你應該附加 auth.basic 中介軟體到你的路由。laravel 中內建了 auth.basic 中介軟體,所以你不需要去定義它:
Route::get('profile', ['middleware' => 'auth.basic', function () { // Only authenticated users may enter... }]); 一旦該中介軟體被附加到路由中,你每次訪問該路由時都會被提示要求認證資訊。預設的,auth.basic 中介軟體使用 email 列作為使用者記錄的使用者名稱。
FastCGI 提示
如果你使用 PHP FastCGI,基於 HTTP 的認證可能無法正常工作。你可以嘗試在 .htaccess 檔案中添加如下內容:
RewriteCond %{HTTP:Authorization} ^(.+)$RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] 無狀態的 HTTP 基本認證
你也可以在使用 HTTP 基本認證時不使用 session 儲存使用者的身份到 cookie。這在基於 API 的認證尤其有效。為了做到這些,你可以定義一個中介軟體,然後調用 onceBasic 方法。如果 onceBasic 方法沒有返迴響應,那麼請求將被進一步傳遞到應用:
接著,在路由中附加中介軟體:
Route::get('api/user', ['middleware' => 'auth.basic.once', function () { // Only authenticated users may enter... }]); 當然,你還需要在 kernel.php 核心中註冊該中介軟體。
密碼重設
資料庫注意事項
大多數的應用都提供了一種使用者重設其忘記密碼的方式。laravel 提供了一種便利的方式來發送密碼提示和提供密碼重設,這樣你就不需要被迫在每個應用都實現一次這種機制。
為了方便開始,請確定你的 App\User 模型實現了 Illuminate\Contracts\Auth\CanResetPassword 契約,laravel 中內建的 App\User 模型中已經實現了這個借口,並且使用了 Illuminate\Auth\Passwords\CanResetPassword trait。
通過遷移產生密碼重設表
接著,必須要建立一個用來儲存重設密碼的 token 的表。laravel 已經提供了這種表的遷移,並且存放在 database/migrations 目錄下,所以,你只需要執行遷移命令:
php artisan migrate
路由
laravel 內建的 Auth\PasswordController 控制器包含了所有重設密碼所需的邏輯。所有提供密碼重設的路由都可以通過 make:auth Artisan 命令來產生:
php artisan make:auth
視圖
當 make:auth 命令被執行時,laravel 會產生所有密碼重設所需要的視圖。這些視圖將被存放在 resources/views/auth/passwords 目錄中,你可以根據自己的需求去修改。
重設密碼之後
一旦你產生了所有重設密碼所需要的路由和視圖之後,你就可以通過在瀏覽器訪問 /password/reset 路由來進行密碼重設。PasswordController 已經包含了發送重設密碼的連結郵件及更新密碼到資料庫的功能。
當密碼被重設之後,使用者會自動登入並且被重新導向到 /home。你可以在 PasswordController 中定義 redirectTo 屬性來定製化重新導向地址:
protected $redirectTo = '/dashboard';
注意,預設的,密碼重設的 token 有效期間只有一個小時,你可以在 config/auth.php 設定檔中修改 expire 選項。
定製
定製化認證守衛
在 auth.php 設定檔中,你可以配置多種 "guards",用於對各種使用者表執行認證動作。你可以在 PasswordController 中使用 $guard 屬性來選擇守衛:
/** * The authentication guard that should be used. * * @var string */ protected $guard = 'admins';
定製化密碼經紀人
在 auth.php 設定檔中,你可以配置多種密碼“brokers”,用於對多種使用者表進行密碼重設。你可以通過在 PasswordController 中添加 $broker 屬性來選擇:
/** * The password broker that should be used. * * @var string */ protected $broker = 'admins';
添加自訂的守衛
你可以在服務容器中使用 Auth 假面的 extend 方法來定義自己的認證守衛:
你應該能從上面的樣本中看到,你應該在 extend 方法中返回一個 Illuminate\Contracts\Auth\Guard 的實現。為了定製化守衛你需要實現這個介面所包含的方法。
一旦你的守衛被定義,你就可以在 auth.php 設定檔的 guards 選項中進行配置:
'guards' => [ 'api' => [ 'driver' => 'jwt', 'provider' => 'users' ],];
添加自訂的使用者提供者
如果你並沒有使用傳統的關聯式資料庫來儲存你的使用者,那麼你需要提供你自己的使用者提供者來擴充 laravel。你可以通過使用 Auth 假面的 provider 方法來定義自己的使用者提供者。你應該在服務提供者中調用該方法:
在你使用 provider 方法註冊完成提供者之後,你需要在 config/auth.php 設定檔中切換你的使用者提供者為新註冊的提供者:
'providers' => [ 'users' => [ 'driver' => 'riak', ],],
然後,你需要在 guards 選項中使用該提供者:
'guards' => [ 'web' => [ 'driver' => 'session', 'provider' => 'users', ],],
使用者提供者契約
Illuminate\Contracts\Auth\UserProvider 的實現僅僅只是管理如何從持續儲存系統中擷取一個 Illuminate\Contracts\Auth\Authenticatable 的實現。如 MySQL,Riak,等等。這個兩個介面實現了 laravel 的持續認證機制而不需要考慮使用者資料是存放在哪裡了或者存放的是什麼類型。
讓我們來看一下 Illuminate\Contracts\Auth\UserProvider 契約:
retrieveById 方法用來接收一個鍵來返回一個使用者,如 MySQL 資料庫中自增的主鍵 ID。被匹配的 ID 應該返回一個 Authenticatable 的實現。
retrieveByToken 方法接收用於識別使用者身份的唯一標識 $identifier 和 remember_token 所儲存“記住我”的 $token。就如上面的方法一樣,該方法應該返回一個 Authenticatable 的實現。
updateRememberToken 方法使用新的 $token 來更新使用者的 remember_token 欄位。這個新的欄位可以是使用者登入成功並且設定了 記住我 而分配的,也可以是使用者登出之後分配的 null。
retrieveByCredentials 方法接收一個數組憑證,它會在使用者嘗試登入時將憑證傳遞給 Auth::attemp 方法。該方法會詢問底層持續存放裝置憑證的匹配情況,一般該方法會使用 where 條件陳述式來進行查詢 $credentials['username'] 類似的匹配狀況。這個方法應該返回一個 UserInterface 的實現。不要在這個方法裡做密碼驗證或者認證。
validateCredentials 方法應該比較所給定的使用者和憑證的匹配情況。比如,這個方法或許會比較 $user-getAuthPassword() 和 Hash::make 後的 $credentials['password']。這個方法應該只做使用者和憑證間的效驗和返回比較情況的布爾值。
可認證的(Authenticatable)契約
剛才我們探討了 UserProvider 中的所有方法,現在讓我們來看一看 Authenticatable 契約,你應該記得,提供者應該從 retrieveById 和 retrieveByCredentials 方法中返回該契約介面的實現:
這個介面相對來說非常簡單。getAuthIdentifierName 方法應該返回使用者的主鍵欄位的名稱。getAuthIdentifier 方法應該返回使用者的主鍵。在 MySQL 中,這個主鍵一般為自增長的主索引值。getAuthPassword 應該返回使用者的經雜湊後的密碼。這個介面使認證系統可以在任何使用者類中運行而不用管你使用的是什麼 ORM 或者其它儲存抽象層。預設的,laravel 在 app 目錄下包含了 User 類,該類就實現了這個契約。所以你可以參照這個類的實現方式。
事件
laravel 在認證進程中提供了各種各樣的事件。你可以在你的 EventServiceProvider 中附加監聽這些事件:
/** * The event listener mappings for the application. * * @var array */ protected $listen = [ 'Illuminate\Auth\Events\Attempting' => [ 'App\Listeners\LogAuthenticationAttempt', ], 'Illuminate\Auth\Events\Login' => [ 'App\Listeners\LogSuccessfulLogin', ], 'Illuminate\Auth\Events\logout' => [ 'App\Listeners\logSuccessfulLogout', ], 'Illuminate\Auth\Events\Lockout' => [ 'App\Listeners\LogLockout', ], ];