# 用戶認證
- [簡介](#introduction)
- [數據庫注意事項](#introduction-database-considerations)
- [認證快速入門](#authentication-quickstart)
- [路由](#included-routing)
- [視圖](#included-views)
- [認證](#included-authenticating)
- [檢索認證用戶](#retrieving-the-authenticated-user)
- [保護路由](#protecting-routes)
- [登錄限制](#login-throttling)
- [手動認證用戶](#authenticating-users)
- [記住用戶](#remembering-users)
- [其它認證方法](#other-authentication-methods)
- [HTTP 基礎認證](#http-basic-authentication)
- [無狀態 HTTP 基礎認證](#stateless-http-basic-authentication)
- [社交認證](https://github.com/laravel/socialite)
- [增加自定義 Guards](#adding-custom-guards)
- [增加自定義用戶提供器](#adding-custom-user-providers)
- [用戶提供器契約](#the-user-provider-contract)
- [認證契約](#the-authenticatable-contract)
- [事件](#events)
<a name="introduction"></a>
## 簡介
> {tip} **想要快點開始?** 只需在新的 Laravel 應用上運行 `php artisan make:auth` 和 `php artisan migrate` 命令。然后可以用瀏覽器訪問 `http://your-app.dev/register` 或者你在程序中定義的其它 URL 。這兩個命令就可以構建好整個認證系統。
Laravel 中實現用戶認證非常簡單。實際上,幾乎所有東西都已經為你配置好了。其配置文件位于 `config/auth.php`,其中包含了用于調整認證服務行為的注釋清晰的選項配置。
其核心是由 Laravel 的認證組件的「看守器」和「提供器」組成。看守器定義了該如何認證每個請求中用戶。例如,Laravel 自帶的 `session` 看守器會使用 session 存儲和 cookies 來維護狀態。
提供器中定義了該如何從持久化的存儲數據中檢索用戶。Laravel 自帶支持使用 Eloquent 和數據庫查詢構造器來檢索用戶。當然,你可以根據需要自定義其他提供器。
不過對大多數應用而言,可能永遠都不需要修改默認身份認證配置。
<a name="introduction-database-considerations"></a>
### 數據庫注意事項
默認情況下,Laravel 在 `app` 目錄中包含了一個 [Eloquent 模型](/docs/{{version}}/eloquent) `App\User`。這個模型和默認的 Eloquent 認證驅動一起使用。如果你的應用不使用 Eloquent,也可以使用 Laravel 查詢構造器的 `database` 認證驅動。
為 `App\User` 模型創建數據庫表結構時,確保密碼字段長度至少為 60 個字符以及默認字符串列長度為 255 個字符。
此外,你要驗證的用戶(或等效的)表要包含一個可空的、長度為 100 的字符串 `remember_token`。這個字段將用于存儲當用戶登錄應用并勾選「記住我」時的令牌。
<a name="authentication-quickstart"></a>
## 認證快速入門
Laravel 自帶幾個預構建的認證控制器,它們被放置在 `App\Http\Controllers\Auth` 命名空間內。`RegisterController` 處理新用戶注冊,`LoginController` 處理用戶認證,`ForgotPasswordController` 處理用于重置密碼的郵件鏈接,而 `ResetPasswordController` 包含重置密碼的邏輯。這些控制器都使用 trait 來引入所必要的方法。對于大多數應用而言,你根本不需要修改這些控制器。
<a name="included-routing"></a>
### 路由
Laravel 提供了一個簡單的命令來快速生成身份驗證所需的路由和視圖:
php artisan make:auth
該命令最好在新的應用下使用,它會生成布局、注冊和登錄視圖以及所有的認證接口的路由。同時它還會生成 `HomeController` 來處理應用的登錄請求。
<a name="included-views"></a>
### 視圖
`php artisan make:auth` 命令會在 `resources/views/auth` 目錄創建所有認證需要的視圖。
同時,`make:auth` 命令還創建了 `resources/views/layouts` 目錄,該目錄包含了應用的基本布局視圖。所有這些視圖都是用 Bootstrap CSS 框架,你也可以根據需要對其自定義。
<a name="included-authenticating"></a>
### 認證
現在你已經為自帶的認證控制器設置好了路由和視圖,可以為應用注冊和驗證新用戶了。你可以簡單地在瀏覽器中訪問應用,因為身份驗證控制器已包含了(通過 traits)驗證現有用戶并將新用戶存儲在數據庫中的邏輯了。
#### 自定義路徑
當用戶成功通過身份認證后,他們會被重定向到 `/home` URI。你可以通過在 `LoginController`、`RegisterController` 和 `ResetPasswordController`中設置 `redirectTo` 屬性來自定義重定向的位置:
protected $redirectTo = '/';
如果重定向路徑需要自定義生成邏輯,你可以定義 `redirectTo` 方法來代替 `redirectTo` 屬性:
protected function redirectTo()
{
return '/path';
}
> {tip} `redirectTo` 方法優先于 `redirectTo` 屬性。
#### 自定義用戶名
Laravel 默認使用 `email` 字段來認證。如果你想用其他字段認證,可以在 `LoginController` 里面定義一個 `username` 方法:
public function username()
{
return 'username';
}
#### 自定義看守器
你還可以自定義用于認證和注冊用戶的「看守器」。要實現這一功能,需要在 `LoginController`、`RegisterController` 和 `ResetPasswordController` 中定義 `guard` 方法。該方法需要返回一個 guard 實例:
use Illuminate\Support\Facades\Auth;
protected function guard()
{
return Auth::guard('guard-name');
}
#### 自定義驗證/存儲
要修改新用戶在應用注冊時所需的表單字段,或者自定義如何將新用戶存儲到數據庫中,你可以修改 `RegisterController` 類。該類負責驗證和創建應用的新用戶。
`RegisterController` 的 `validator` 方法包含了應用驗證新用戶的規則,你可以按需要自定義該方法。
`RegisterController` 的 `create` 方法負責使用 [Eloquent ORM](/docs/{{version}}/eloquent) 在數據庫中創建新的 `App\User` 記錄。你可以根據數據庫的需要自定義該方法。
<a name="retrieving-the-authenticated-user"></a>
### 檢索認證用戶
你可以通過 `Auth` facade 來訪問認證的用戶:
use Illuminate\Support\Facades\Auth;
// 獲取當前已認證的用戶...
$user = Auth::user();
// 獲取當前已認證的用戶 ID...
$id = Auth::id();
或者,你還可以通過 `Illuminate\Http\Request` 實例來訪問已認證的用戶。請記住,類型提示的類會被自動注入到您的控制器方法中:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class ProfileController extends Controller
{
/**
* 更新用戶的簡介。
*
* @param Request $request
* @return Response
*/
public function update(Request $request)
{
// $request->user() 返回已認證的用戶的實例...
}
}
#### 確定當前用戶是否認證
你可以使用 `Auth` facade 的 `check` 方法來檢查用戶是否登錄,如果已經認證,將會返回 `true`:
use Illuminate\Support\Facades\Auth;
if (Auth::check()) {
// 用戶已登錄...
}
> {tip} 即使可以使用 `check` 方法確定用戶是否被認證,在允許用戶訪問某些路由/控制器之前,通常還是會使用中間件來驗證用戶是否進行身份驗證。要了解更多信息,請查看有關 [保護路由](/docs/{{version}}/authentication#protecting-routes) 的文檔。
<a name="protecting-routes"></a>
### 保護路由
[路由中間件](/docs/{{version}}/middleware) 用于只允許通過認證的用戶訪問指定的路由。Laravel 自帶了在 `Illuminate\Auth\Middleware\Authenticate` 中定義的 `auth` 中間件。由于這個中間件已經在 HTTP 內核中注冊,所以只需要將中間件附加到路由定義中:
Route::get('profile', function () {
// 只有認證過的用戶可以...
})->middleware('auth');
當然,如果使用 [控制器](/docs/{{version}}/controllers),則可以在構造器中調用 `middleware` 方法來代替在路由中直接定義:
public function __construct()
{
$this->middleware('auth');
}
#### 指定看守器
將 `auth` 中間件添加到路由時,還需要指定使用哪個看守器來認證用戶。指定的看守器對應配置文件 `auth.php` 中 `guards` 數組的某個鍵:
public function __construct()
{
$this->middleware('auth:api');
}
<a name="login-throttling"></a>
### 登錄限制
Laravel 內置的控制器 `LoginController` 已經包含了 `Illuminate\Foundation\Auth\ThrottlesLogins` trait。默認情況下,如果用戶在進行幾次嘗試后仍不能提供正確的憑證,該用戶將在一分鐘內無法進行登錄。這個限制基于用戶的用戶名/郵件地址和 IP 地址。
<a name="authenticating-users"></a>
## 手動認證用戶
當然,不一定要使用 Laravel 內置的認證控制器。如果選擇刪除這些控制器,你可以直接調用 Laravel 的認證類來管理用戶認證。別擔心,這簡單得很。
我們可以通過 `Auth` [facade](/docs/{{version}}/facades) 來訪問 Laravel 的認證服務,因此需要確認類的頂部引入了 `Auth` facade。接下來讓我們看一下 `attempt` 方法:
<?php
namespace App\Http\Controllers;
use Illuminate\Support\Facades\Auth;
class LoginController extends Controller
{
/**
* 處理身份認證嘗試.
*
* @return Response
*/
public function authenticate()
{
if (Auth::attempt(['email' => $email, 'password' => $password])) {
// 認證通過...
return redirect()->intended('dashboard');
}
}
}
`attempt` 方法會接受一個鍵值對數組作為其第一個參數。這個數組的值將用來在數據庫表中查找用戶。所以在上面的例子中,用戶將被 `email` 字段的值檢索。如果用戶被找到了,數據庫中存儲的散列密碼將與通過數組傳遞給方法的散列 `password` 進行比較。 如果兩個散列密碼匹配,就會為用戶啟動一個已認證的會話。
如果認證成功,`attempt` 方法將返回 `true`,反之則返回 `false`。
在由身份驗證中間件攔截之前,重定向器上的 `intended` 方法將重定向到用戶嘗試訪問的 URL。如果該 URL 無效,會給該方法傳遞回退 URI。
#### 指定額外條件
除了用戶的電子郵件和密碼之外,你還可以向身份驗證查詢添加額外的條件。例如,我們可能會驗證用戶是否被標記為「活動」狀態:
if (Auth::attempt(['email' => $email, 'password' => $password, 'active' => 1])) {
// 用戶處于活動狀態,不被暫停,并且存在。
}
> {note} 在這些例子中,`email` 不是必需的選項,僅作為示例使用。你應該使用與數據庫中的「用戶名」對應的任何字段的名稱。
#### 訪問指定的看守器實例
可以通過 `Auth` facade 的 `guard` 方法來指定要使用哪個看守器實例。這允許你使用完全獨立的可認證模型或用戶表來管理應用的抽離出來的身份驗證。
傳遞給 `guard` 方法的看守器名稱應該與 `auth.php` 配置文件中 guards 中的其中一個值相對應:
if (Auth::guard('admin')->attempt($credentials)) {
//
}
#### 注銷用戶
要讓用戶從應用中注銷,可以在 `Auth` facade 上使用 `logout` 方法。這會清除用戶會話中的身份驗證信息:
Auth::logout();
<a name="remembering-users"></a>
### 記住用戶
如果你想要在應用中提供「記住我」的功能 , 則可以傳遞一個布爾值作為 `attempt` 方法的第二個參數,這會使在用戶手動注銷前一直保持已驗證狀態。當然,`users` 數據表必須包含 `remember_token` 字段,這是用來保存「記住我」的令牌。
if (Auth::attempt(['email' => $email, 'password' => $password], $remember)) {
// 這個用戶被記住了...
}
> {tip} 如果你使用 Laravel 內置的 `LoginController`,則「記住」用戶的邏輯已經由控制器使用的 traits 來實現。
如果你「記住」用戶,可以使用 `viaRemember` 方法來檢查這個用戶是否使用「記住我」 cookie 進行認證:
if (Auth::viaRemember()) {
//
}
<a name="other-authentication-methods"></a>
### 其它認證方法
#### 驗證用戶實例
如果需要將現有用戶實例記錄到應用,可以使用用戶實例調用 `login` 方法。給定的對象必須實現了 `Illuminate\Contracts\Auth\Authenticatable` [契約](/docs/{{version}}/contracts) 。當然,Laravel 自帶的 `App\User` 模型已經實現了這個接口:
Auth::login($user);
// 登錄并且「記住」給定用戶...
Auth::login($user, true);
當然,你也可以指定要使用的看守器實例:
Auth::guard('admin')->login($user);
#### 通過 ID 驗證用戶
你可以使用 `loginUsingId` 方法通過其 ID 將用戶記錄到應用中。這個方法只接受要驗證的用戶的主鍵:
Auth::loginUsingId(1);
//登錄并且「記住」給定的用戶...
Auth::loginUsingId(1, true);
#### 僅驗證用戶一次
你可以使用 `once` 方法將用戶記錄到單個請求的應用中。不會使用任何會話或 cookies, 這個對于構建無狀態的 API 非常的有用:
if (Auth::once($credentials)) {
//
}
<a name="http-basic-authentication"></a>
## HTTP 基礎認證
[HTTP 基礎認證](https://en.wikipedia.org/wiki/Basic_access_authentication) 提供一種快速方式來認證應用的用戶,而且不需要設置專用的「登錄」頁面。開始之前,先把 `auth.basic` [中間件](/docs/{{version}}/middleware) 添加到你的路由。`auth.basic` 中間件已經被包含在 Laravel 框架中,所以你不需要定義它:
Route::get('profile', function () {
// 只有認證過的用戶可進入...
})->middleware('auth.basic');
中間件被增加到路由后,在瀏覽器中訪問路由時,將自動提示你輸入憑證。默認情況下,`auth.basic` 中間件將會使用用戶記錄上的 `email` 字段作為「用戶名」。
#### FastCGI 的注意事項
如果使用了 PHP FastCGI,HTTP 基礎認證可能無法正常工作。你需要將下面這幾行加入你 `.htaccess` 文件中:
RewriteCond %{HTTP:Authorization} ^(.+)$
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
<a name="stateless-http-basic-authentication"></a>
### 無狀態 HTTP 基礎認證
你可以使用 HTTP 基礎認證,而不在會話中設置用戶標識符 cookie,這對于 API 認證來說特別有用。為此,請 [定義一個中間件](/docs/{{version}}/middleware) 并調用 `onceBasic` 方法。如果 `onceBasic` 方法沒有返回任何響應的話,這個請求可以進一步傳遞到應用程序中:
<?php
namespace Illuminate\Auth\Middleware;
use Illuminate\Support\Facades\Auth;
class AuthenticateOnceWithBasicAuth
{
/**
* 處理傳入的請求。
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, $next)
{
return Auth::onceBasic() ?: $next($request);
}
}
接著,[注冊路由中間件](/docs/{{version}}/middleware#registering-middleware) ,然后將它附加到路由:
Route::get('api/user', function () {
// 只有認證過的用戶可以進入...
})->middleware('auth.basic.once');
<a name="adding-custom-guards"></a>
## 增加自定義的看守器
你可以使用 `Auth` facade 的 `extend` 方法來定義自己的身份驗證提供器。 你需要在 [服務提供器](/docs/{{version}}/providers) 中調用這個提供器。由于 Laravel 已經配備了 `AuthServiceProvider`,我們可以把代碼放在這個提供器中:
<?php
namespace App\Providers;
/use App\Services\Auth\JwtGuard;
use Illuminate\Support\Facades\Auth;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
class AuthServiceProvider extends ServiceProvider
{
/**
* 注冊任意應用認證/授權服務。
*
* @return void
*/
public function boot()
{
$this->registerPolicies();
Auth::extend('jwt', function ($app, $name, array $config) {
// 返回一個 Illuminate\Contracts\Auth\Guard 實例...
return new JwtGuard(Auth::createUserProvider($config['provider']));
});
}
}
正如上面的代碼所示,傳遞給 `extend` 方法的回調應該返回 `Illuminate\Contracts\Auth\Guard` 的實現的實例。 這個接口包含你需要實現的方法來定義一個自定義看守器。定義完之后,你可以在 `auth.php` 配置文件的 `guards` 配置中使用這個看守器:
'guards' => [
'api' => [
'driver' => 'jwt',
'provider' => 'users',
],
],
<a name="adding-custom-user-providers"></a>
## 添加自定義用戶提供器
如果你沒有使用傳統的關系型數據庫來存儲用戶信息,則需要使用自己的用戶認證提供器來擴展 Laravel。我們使用 `Auth` facade 上的 `provider` 方法定義自定義用戶提供器:
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Auth;
use App\Extensions\RiakUserProvider;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
class AuthServiceProvider extends ServiceProvider
{
/**
* 注冊任何應用認證/授權服務。
*
* @return void
*/
public function boot()
{
$this->registerPolicies();
Auth::provider('riak', function ($app, array $config) {
// 返回 Illuminate\Contracts\Auth\UserProvider 實例...
return new RiakUserProvider($app->make('riak.connection'));
});
}
}
使用 `provider` 方法注冊用戶提供器后,你可以在配置文件 `auth.php`中切換到新的用戶提供器。首先,定義一個使用新驅動的 `provider`:
'providers' => [
'users' => [
'driver' => 'riak',
],
],
最后,你可以在 `guards` 配置中使用這個提供器:
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
],
<a name="the-user-provider-contract"></a>
### 用戶提供器契約
`Illuminate\Contracts\Auth\UserProvider` 的實現只負責從永久存儲系統(如 MySQL、Riak 等)中獲取 `Illuminate\Contracts\Auth\Authenticatable` 的實現實例。這兩個接口允許 Laravel 認證機制繼續運行,無論用戶數據如何被存儲或使用什么類型的類實現它。
讓我們來看看 `Illuminate\Contracts\Auth\UserProvider` 契約:
<?php
namespace Illuminate\Contracts\Auth;
interface UserProvider {
public function retrieveById($identifier);
public function retrieveByToken($identifier, $token);
public function updateRememberToken(Authenticatable $user, $token);
public function retrieveByCredentials(array $credentials);
public function validateCredentials(Authenticatable $user, array $credentials);
}
`retrieveById` 函數通常接收代表用戶的鍵,例如 MySQL 數據庫中自增的 ID。應該通過該方法檢索和返回與 ID 匹配的`Authenticatable` 的實現實例。
`retrieveByToken` 函數通過其唯一的 `$identifier` 來檢索用戶,并將「記住我」 `$token` 存儲在 `remember_token` 字段中。與以前的方法一樣,應該返回 `Authenticatable` 實現的實例。
`updateRememberToken` 方法使用新的 `$token` 更新了 `$user` 的 `remember_token` 字段。當使用「記住我」嘗試登錄成功時,或用戶登出時,這個新的令牌可以是全新的。
在嘗試登錄應用程序時,`retrieveByCredentials` 方法接收傳遞給 `Auth::attempt` 方法的憑據數組。然后該方法將「查詢」底層持久存儲匹配用戶的這些憑據。通常,此方法會在 `$credentials['username']` 上運行「where」條件的查詢。這個方法應該需要返回 `Authenticatable` 的實現的實例。**此方法不應該嘗試任何密碼驗證或認證。**
`validateCredentials` 方法將給定的 `$user` 和 `$credentials` 進行匹配,以此來驗證用戶。例如,這個方法可以使用 `Hash::check` 比較 `$user->getAuthPassword()` 的值及 `$credentials['password']` 的值。此方法通過返回 `true` 或 `false` 來顯示密碼是否有效。
<a name="the-authenticatable-contract"></a>
### 認證契約
現在我們已經探討了 `UserProvider` 中的每個方法,讓我們來看看 `Authenticatable` 契約。記住,提供器需要從 `retrieveById` 和 `retrieveByCredentials` 方法來返回這個接口的實現:
<?php
namespace Illuminate\Contracts\Auth;
interface Authenticatable {
public function getAuthIdentifierName();
public function getAuthIdentifier();
public function getAuthPassword();
public function getRememberToken();
public function setRememberToken($value);
public function getRememberTokenName();
}
這個接口很簡單。`getAuthIdentifierName` 方法返回用戶的「主鍵」字段的名稱,而 `getAuthIdentifier` 方法返回用戶的「主鍵」。重申一次,在 MySQL 后臺,這個主鍵是指自增的主鍵。`getAuthPassword` 應該要返回用戶的散列密碼。這個接口允許認證系統和任何用戶類一起工作,不用管你在使用什么 ORM 或存儲抽象層。默認情況下,Laravel 的 `app` 目錄中包含一個 `User` 類來實現此接口,因此你可以參考這個類來實現一個實例。
<a name="events"></a>
## 事件
Laravel 在認證過程中引發了各種各樣的 [事件](/docs/{{version}}/events)。你可以在 `EventServiceProvider` 中對這些事件做監聽:
/**
* 應用程序的事件監聽器映射。
*
* @var array
*/
protected $listen = [
'Illuminate\Auth\Events\Registered' => [
'App\Listeners\LogRegisteredUser',
],
'Illuminate\Auth\Events\Attempting' => [
'App\Listeners\LogAuthenticationAttempt',
],
'Illuminate\Auth\Events\Authenticated' => [
'App\Listeners\LogAuthenticated',
],
'Illuminate\Auth\Events\Login' => [
'App\Listeners\LogSuccessfulLogin',
],
'Illuminate\Auth\Events\Failed' => [
'App\Listeners\LogFailedLogin',
],
'Illuminate\Auth\Events\Logout' => [
'App\Listeners\LogSuccessfulLogout',
],
'Illuminate\Auth\Events\Lockout' => [
'App\Listeners\LogLockout',
],
];
## 譯者署名
| 用戶名 | 頭像 | 職能 | 簽名 |
| --- | --- | --- | --- |
| [@iwzh](https://github.com/iwzh) | <img class="avatar-66 rm-style" src="https://dn-phphub.qbox.me/uploads/avatars/3762_1456807721.jpeg?imageView2/1/w/200/h/200"> | 翻譯 | 碼不能停 [@iwzh](https://github.com/iwzh) at Github |
---
> {note} 歡迎任何形式的轉載,但請務必注明出處,尊重他人勞動共創開源社區。
>
> 轉載請注明:本文檔由 Laravel China 社區 [laravel-china.org](https://laravel-china.org) 組織翻譯,詳見 [翻譯召集帖](https://laravel-china.org/topics/5756/laravel-55-document-translation-call-come-and-join-the-translation)。
>
> 文檔永久地址: https://d.laravel-china.org
- 說明
- 翻譯說明
- 發行說明
- 升級說明
- 貢獻導引
- 入門指南
- 安裝
- 配置信息
- 文件夾結構
- HomeStead
- Valet
- 核心架構
- 請求周期
- 服務容器
- 服務提供者
- 門面(Facades)
- Contracts
- 基礎功能
- 路由
- 中間件
- CSRF 保護
- 控制器
- 請求
- 響應
- 視圖
- 重定向
- Session
- 表單驗證
- 錯誤與日志
- 前端開發
- Blade 模板
- 本地化
- 前端指南
- 編輯資源 Mix
- 安全
- 用戶認證
- API認證
- 用戶授權
- 加密解密
- 哈希
- 重置密碼
- 綜合話題
- Artisan 命令行
- 廣播系統
- 緩存系統
- 集合
- 事件系統
- 文件存儲
- 輔助函數
- 郵件發送
- 消息通知
- 擴展包開發
- 隊列
- 任務調度
- 數據庫
- 快速入門
- 查詢構造器
- 分頁
- 數據庫遷移
- 數據填充
- Redis
- Eloquent ORM
- 快速入門
- 模型關聯
- Eloquent 集合
- 修改器
- API 資源
- 序列化
- 測試
- 快速入門
- HTTP 測試
- 瀏覽器測試 Dusk
- 數據庫測試
- 測試模擬器
- 官方擴展包
- Cashier 交易工具包
- Envoy 部署工具
- Horizon
- Passport OAuth 認證
- Scout 全文搜索
- Socialite 社交化登錄
- 交流說明