[TOC]
# 用戶認證
## 用戶認證快速指南
### 構建認證系統
```
$ php artisan make:auth
$ php artisan migrate
```
### 生成路由信息
```
// vendor/laravel/framework/src/Illuminate/Routing/Router.php auth方法
Auth::routes();
Route::get('/home', 'HomeController@index')->name('home');
// 禁用注冊,刪除 RegisterController 并修改路由聲明
Auth::routes(['register' => false]);
```
### 生成控制器文件
```
app\Http\Controllers\HomeController.php
```
### 生成視圖文件
```
resources/views/auth
resources/views/layouts
resources/views/home.blade.php
```
### 自定義重定向路徑
>[success] 默認重定向到 /home,修改以下控制器的 redirectTo 屬性修改重定向
LoginController // 登錄后跳轉
RegisterController // 注冊后跳轉
ResetPasswordController // 重置密碼后跳轉
VerificationController // 驗證后跳轉
```
protected $redirectTo = '/';
// 自定義生成重定向路徑的邏輯,redirectTo 方法優先于 redirectTo 屬性。
protected function redirectTo()
{
return '/path';
}
```
> 修改中間件 RedirectIfAuthenticated 的 handle 方法,用來重定向驗證前的頁面:/login、/register、/email/verify
### 自定義驗證的用戶字段
```
// 使用 username 替換 email 字段來認證
public function username()
{
return 'username';
}
```
### 自定義看守器
```
// 自定義用戶認證和注冊的「看守器」
// 需要在 LoginController,RegisterController,ResetPasswordController 定義 guard 方法
use Illuminate\Support\Facades\Auth;
protected function guard()
{
return Auth::guard('guard-name');
}
```
### 自定義驗證 / 存儲
> 自定義 RegisterController 類,可以自定義注冊信息,驗證規則,存儲數據方式等。
### 檢索認證用戶
```
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() 返回一個認證用戶實例...
}
}
```
### 確定當前用戶是否已經認證
```
use Illuminate\Support\Facades\Auth;
if (Auth::check()) {
// 用戶已經登錄了...
}
```
### 保護路由
```
// 只允許通過認證的用戶訪問
Route::get('profile', function () {
// 只有認證過的用戶可以進入...
})->middleware('auth');
// 在控制器使用中間件
public function __construct()
{
$this->middleware('auth');
}
```
### 重定向未認證的用戶
```
// 修改 app/Http/Middleware/Authenticate.php 文件中的 redirectTo 函數
protected function redirectTo($request)
{
return route('login');
}
```
### 指定看守器
```
// 指定的看守器應該對應 auth.php 配置文件中 guards 數組中的的一個鍵
public function __construct()
{
$this->middleware('auth:api');
}
```
### 登錄限流
如果你使用 Laravel 內置的 `LoginController` 類,`Illuminate\Foundation\Auth\ThrottlesLogins` trait 已經包含在該控制器中了。默認情況下,如果用戶多次嘗試卻無法提供正確的登錄憑據,那么該用戶在一分鐘內將不能再次嘗試登錄。這種限流策略基于用戶的用戶名/郵箱地址及其 IP 地址的唯一性。
## 手動驗證用戶
```
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class LoginController extends Controller
{
/**
* 處理身份驗證嘗試。
*
* @param \Illuminate\Http\Request $request
*
* @return Response
*/
public function authenticate(Request $request)
{
$credentials = $request->only('email', 'password');
if (Auth::attempt($credentials)) {
// 身份驗證通過...
return redirect()->intended('dashboard');
}
}
}
```
### 指定額外條件
```
if (Auth::attempt(['email' => $email, 'password' => $password, 'active' => 1])) {
// 用戶存在,已激活且未被禁用。
}
```
訪問指定的看守器實例
```
// 看守器名稱需要在 auth.php 配置中設置過
if (Auth::guard('admin')->attempt($credentials)) {
//
}
```
登出
```
Auth::logout();
```
記住用戶
```
// 設置 attempt 方法的第二參數,永久保持用戶登錄狀態
// 用戶表需要包含字符串類型的 remember_token 列用于存儲令牌
if (Auth::attempt(['email' => $email, 'password' => $password], $remember)) {
// 用戶被記住...
}
```
其它身份驗證方法
```
// 驗證用戶實例,將已經存在的用戶登入應用
Auth::login($user);
// 登錄并「記住」給定的用戶...
Auth::login($user, true);
// 通過 ID 驗證用戶身份,通過 ID 將用戶登錄到應用
Auth::loginUsingId(1);
// 登錄并「記住」給定用戶...
Auth::loginUsingId(1, true);
// 僅驗證一次用戶身份,在單次請求中將用戶登錄到應用
// 不使用 session 或 cookies,此方法有助于構建一個無狀態 API
if (Auth::once($credentials)) {
//
}
```
## HTTP 基礎認證
```
// 使用 auth.basic 中間件快速驗證用戶,無需設置「登錄」頁面,默認 email 字段
Route::get('profile', function () {
// 只有認證過的用戶可以進入...
})->middleware('auth.basic');
```
### FastCGI 的注意事項
```
// HTTP 基礎認證可能無法正常工作,請設置 .htaccess 文件
RewriteCond %{HTTP:Authorization} ^(.+)$
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
```
無狀態 HTTP 基礎認證
```
// 使用 HTTP 基礎身份驗證,而無需在會話中設置用戶標識符 cookie,這對 API 的身份驗證特別有用。
<?php
namespace App\Http\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);
}
}
// 注冊路由中間件
Route::get('api/user', function () {
// 只有認證過的用戶可以進入...
})->middleware('auth.basic.once');
```
## 退出
```
use Illuminate\Support\Facades\Auth;
Auth::logout();
```
### 讓其它設備上的 Session 失效
```
// 確保 Illuminate\Session\Middleware\AuthenticateSession 中間件在
// app/Http/Kernel.php 類中的 web 中間件組中
'web' => [
// ...
\Illuminate\Session\Middleware\AuthenticateSession::class,
// ...
],
// 將其它設備上的用戶 Session 注銷
use Illuminate\Support\Facades\Auth;
Auth::logoutOtherDevices($password);
```
## 添加自定義的看守器
```
<?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']));
});
}
}
// 在 auth.php 配置文件的 guards 配置中使用這個看守器
'guards' => [
'api' => [
'driver' => 'jwt',
'provider' => 'users',
],
],
```
### 請求閉包看守器
```
use App\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
/**
* 注冊任意應用認證/授權服務。
*
* @return void
*/
public function boot()
{
$this->registerPolicies();
Auth::viaRequest('custom-token', function ($request) {
return User::where('token', $request->token)->first();
});
}
// 在 auth.php 配置文件的 guards 配置中使用這個看守器
'guards' => [
'api' => [
'driver' => 'custom-token',
],
],
```
## 添加自定義用戶提供器
```
// 如果不使用傳統的關系數據庫存儲用戶,就需要使用自己的身份驗證用戶提供器擴展 Lavare
<?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
{
/**
* 注冊任意應用身份驗證 / 授權服務
* Register any application authentication / authorization services.
*
* @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'));
});
}
}
// 在 auth.php 配置文件中切換到新的用戶提供器
'providers' => [
'users' => [
'driver' => 'riak',
],
],
// 在 guards 配置中使用這個提供器
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
],
```
### 用戶提供器契約
`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);
}
```
`retrieveByToken` 函數通過用戶的唯一 `$identifier` 和存儲在 `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` ,以表明用戶密碼是否有效。
身份驗證契約
我們已經剖析了 `UserProvider` 的每個方法。下面再來看看 `Authenticatable` 契約。切記,用戶提供器的 `retrieveById`、 `retrieveByToken` 和 `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` 方法應該返回用戶的哈希密碼。此接口允許身份驗證系統與任一 User 類一直工作,不管使用的是哪種 ORM 或抽象存儲層。默認情況下,Laravel 的 `app` 目錄會包含一個實現了此接口的 `User` 類,你可以以這個實現示例作為參考。
## 事件
```
// 在 app/Providers/EventServiceProvider.php 中添加事件監聽器
// $ php artisan make:listener LogSuccessfulLogin
/**
* 應用的事件監聽器映射。
*
* @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',
],
'Illuminate\Auth\Events\PasswordReset' => [
'App\Listeners\LogPasswordReset',
],
];
```
- 入門指南
- 安裝
- 部署
- 基礎功能
- 路由
- 中間件
- CSRF 保護
- 控制器
- 請求
- 響應
- 視圖
- URL
- Session
- 表單驗證
- 錯誤
- 日志
- 前端開發
- Blade 模板
- 本地化
- 腳手架
- 編譯資源 Mix
- 安全相關
- 用戶認證
- API 認證
- 綜合話題
- 命令行
- 廣播
- 緩存
- 集合
- 事件
- 文件存儲
- 輔助函數
- 郵件發送
- 消息通知
- 擴展包開發
- 隊列
- 任務調度
- 數據庫
- 快速入門
- 查詢構造器
- 分頁
- 數據庫遷移
- 數據填充
- Redis
- Eloquent ORM
- 快速入門
- 速查表
- Artisan
- Auth
- Blade
- Cache
- Collection
- Composer
- Config
- Container
- Cookie
- DB
- Environment
- Event
- File
- Helper
- Input
- Lang
- Log
- Model
- Pagination
- Queue
- Redirect
- Request
- Response
- Route
- SSH
- Schema
- Security
- Session
- Storage
- String
- URL
- UnitTest
- Validation
- View