认证
介绍
想快速开始吗? 只需在一个新的 Laravel 应用程序中运行 php artisan make:auth
和 php artisan migrate
。然后,在浏览器中导航到 http://your-app.dev/register
或分配给您的应用程序的任何其他 URL。这两个命令将负责为您搭建整个认证系统!
Laravel 使实现认证变得非常简单。实际上,几乎所有内容都为您开箱即用地配置好了。认证配置文件位于 config/auth.php
,其中包含多个文档良好的选项,用于调整认证服务的行为。
在其核心,Laravel 的认证功能由“守卫”和“提供者”组成。守卫定义了如何为每个请求认证用户。例如,Laravel 附带一个 session
守卫,它使用会话存储和 cookies 来维护状态。
提供者定义了如何从持久存储中检索用户。Laravel 附带支持使用 Eloquent 和数据库查询构建器检索用户。但是,您可以根据应用程序的需要定义其他提供者。
如果现在听起来有些困惑,不用担心!许多应用程序永远不需要修改默认的认证配置。
数据库注意事项
默认情况下,Laravel 在您的 app
目录中包含一个 App\User
Eloquent 模型。此模型可与默认的 Eloquent 认证驱动程序一起使用。如果您的应用程序不使用 Eloquent,您可以使用 database
认证驱动程序,该驱动程序使用 Laravel 查询构建器。
在为 App\User
模型构建数据库架构时,请确保密码列至少为 60 个字符长。保持默认的字符串列长度为 255 个字符是一个不错的选择。
此外,您应验证您的 users
(或等效)表包含一个可为空的、长度为 100 个字符的字符串 remember_token
列。此列将用于存储选择“记住我”选项登录到应用程序的用户的令牌。
认证快速入门
Laravel 附带几个预构建的认证控制器,这些控制器位于 App\Http\Controllers\Auth
命名空间中。RegisterController
处理新用户注册,LoginController
处理认证,ForgotPasswordController
处理发送重置密码链接的电子邮件,ResetPasswordController
包含重置密码的逻辑。这些控制器中的每一个都使用一个 trait 来包含其必要的方法。对于许多应用程序,您根本不需要修改这些控制器。
路由
Laravel 提供了一种快速的方法来使用一个简单的命令为认证搭建所需的所有路由和视图:
php artisan make:auth
此命令应在新应用程序上使用,并将安装一个布局视图、注册和登录视图,以及所有认证端点的路由。还将生成一个 HomeController
来处理登录后对应用程序仪表板的请求。
视图
如前一节所述,php artisan make:auth
命令将创建您需要的所有认证视图,并将它们放置在 resources/views/auth
目录中。
make:auth
命令还将创建一个 resources/views/layouts
目录,其中包含应用程序的基本布局。所有这些视图都使用 Bootstrap CSS 框架,但您可以根据需要自由定制它们。
认证
现在您已经为包含的认证控制器设置了路由和视图,您可以为应用程序注册和认证新用户了!您可以简单地在浏览器中访问您的应用程序,因为认证控制器已经包含了通过其 trait 认证现有用户并将新用户存储到数据库中的逻辑。
路径自定义
当用户成功认证后,他们将被重定向到 /home
URI。您可以通过在 LoginController
、RegisterController
和 ResetPasswordController
上定义一个 redirectTo
属性来自定义认证后的重定向位置:
protected $redirectTo = '/';
如果重定向路径需要自定义生成逻辑,您可以定义一个 redirectTo
方法而不是 redirectTo
属性:
protected function redirectTo()
{
//
}
redirectTo
方法的优先级高于 redirectTo
属性。
用户名自定义
默认情况下,Laravel 使用 email
字段进行认证。如果您想自定义此字段,可以在 LoginController
上定义一个 username
方法:
public function username()
{
return 'username';
}
守卫自定义
您还可以自定义用于认证和注册用户的“守卫”。要开始,请在 LoginController
、RegisterController
和 ResetPasswordController
上定义一个 guard
方法。该方法应返回一个守卫实例:
use Illuminate\Support\Facades\Auth;
protected function guard()
{
return Auth::guard('guard-name');
}
验证/存储自定义
要修改新用户注册时所需的表单字段,或自定义新用户如何存储到数据库中,您可以修改 RegisterController
类。此类负责验证和创建应用程序的新用户。
RegisterController
的 validator
方法包含应用程序新用户的验证规则。您可以根据需要自由修改此方法。
RegisterController
的 create
方法负责使用 Eloquent ORM 在数据库中创建新的 App\User
记录。您可以根据数据库的需要自由修改此方法。
检索已认证的用户
您可以通过 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()) {
// 用户已登录...
}
即使可以使用 check
方法确定用户是否已认证,您通常会使用中间件来验证用户在允许用户访问某些路由/控制器之前是否已认证。要了解更多信息,请查看有关保护路由的文档。
保护路由
路由中间件 可用于仅允许已认证的用户访问给定路由。Laravel 附带一个 auth
中间件,该中间件在 Illuminate\Auth\Middleware\Authenticate
中定义。由于此中间件已在您的 HTTP 内核中注册,您只需将中间件附加到路由定义即可:
Route::get('profile', function () {
// 只有已认证的用户可以进入...
})->middleware('auth');
当然,如果您使用 控制器,您可以从控制器的构造函数中调用 middleware
方法,而不是直接在路由定义中附加它:
public function __construct()
{
$this->middleware('auth');
}
指定守卫
在将 auth
中间件附加到路由时,您还可以指定应使用哪个守卫来认证用户。指定的守卫应对应于 auth.php
配置文件中 guards
数组中的一个键:
public function __construct()
{
$this->middleware('auth:api');
}
登录节流
如果您使用 Laravel 的内置 LoginController
类,Illuminate\Foundation\Auth\ThrottlesLogins
trait 将已包含在您的控制器中。默认情况下,如果用户在多次尝试后未能提供正确的凭据,则用户将无法登录一分钟。节流是针对用户的用户名/电子邮件地址和他们的 IP 地址唯一的。
手动认证用户
当然,您不需要使用 Laravel 附带的认证控制器。如果您选择删除这些控制器,您将需要直接使用 Laravel 认证类来管理用户认证。别担心,这很简单!
我们将通过 Auth
facade 访问 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。可以为此方法提供一个备用 URI,以防预期的目的地不可用。
指定附加条件
如果您愿意,您还可以在用户的电子邮件和密码之外为认证查询添加额外条件。例如,我们可以验证用户是否标记为“活跃”:
if (Auth::attempt(['email' => $email, 'password' => $password, 'active' => 1])) {
// 用户是活跃的,没有被暂停,并且存在。
}
在这些示例中,email
不是必需的选项,它仅用作示例。您应该使用与数据库中“用户名”对应的列名。
访问特定守卫实例
您可以使用 Auth
facade 上的 guard
方法指定要使用的守卫实例。这允许您使用完全独立的可认证模型或用户表来管理应用程序不同部分的认证。
传递给 guard
方法的守卫名称应对应于 auth.php
配置文件中配置的一个守卫:
if (Auth::guard('admin')->attempt($credentials)) {
//
}
注销
要将用户从应用程序中注销,您可以使用 Auth
facade 上的 logout
方法。这将清除用户会话中的认证信息:
Auth::logout();
记住用户
如果您希望在应用程序中提供“记住我”功能,可以将布尔值作为 attempt
方法的第二个参数传递,这将使用户无限期地保持认证状态,或直到他们手动注销。当然,您的 users
表必须包含字符串 remember_token
列,该列将用于存储“记住我”令牌。
if (Auth::attempt(['email' => $email, 'password' => $password], $remember)) {
// 用户正在被记住...
}
如果您使用 Laravel 附带的内置 LoginController
,控制器使用的 trait 已经实现了“记住”用户的正确逻辑。
如果您正在“记住”用户,可以使用 viaRemember
方法来确定用户是否使用“记住我”cookie 进行认证:
if (Auth::viaRemember()) {
//
}
其他认证方法
认证用户实例
如果您需要将现有用户实例登录到应用程序中,可以使用用户实例调用 login
方法。给定的对象必须是 Illuminate\Contracts\Auth\Authenticatable
契约 的实现。当然,Laravel 附带的 App\User
模型已经实现了此接口:
Auth::login($user);
// 登录并“记住”给定用户...
Auth::login($user, true);
当然,您可以指定要使用的守卫实例:
Auth::guard('admin')->login($user);
通过 ID 认证用户
要通过用户的 ID 将用户登录到应用程序中,可以使用 loginUsingId
方法。此方法只接受您希望认证的用户的主键:
Auth::loginUsingId(1);
// 登录并“记住”给定用户...
Auth::loginUsingId(1, true);
一次性认证用户
您可以使用 once
方法将用户登录到应用程序中以进行单个请求。不会使用会话或 cookies,这意味着此方法在构建无状态 API 时可能会有所帮助:
if (Auth::once($credentials)) {
//
}
HTTP基本认证
HTTP基本认证 提供了一种快速的方法来认证应用程序的用户,而无需设置专用的“登录”页面。要开始,请将 auth.basic
中间件 附加到您的路由。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}]
无状态HTTP基本认证
您还可以使用 HTTP 基本认证而不在会话中设置用户标识符 cookie,这对于 API 认证特别有用。为此,定义一个中间件 来调用 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);
}
}
接下来,注册路由中间件 并将其附加到路由:
Route::get('api/user', function () {
// 只有已认证的用户可以进入...
})->middleware('auth.basic.once');
添加自定义守卫
您可以使用 Auth
facade 上的 extend
方法定义自己的认证守卫。您应将此调用放在 服务提供者 中。由于 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',
],
],
添加自定义用户提供者
如果您不使用传统的关系数据库来存储用户,您将需要使用自己的认证用户提供者扩展 Laravel。我们将使用 Auth
facade 上的 provider
方法来定义自定义用户提供者:
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Auth;
use App\Extensions\RiakUserProvider;
use Illuminate\Support\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',
],
],
用户提供者契约
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
。新令牌可以是成功“记住我”登录尝试时分配的新令牌,或者是用户注销时的 null
。
retrieveByCredentials
方法接收在尝试登录应用程序时传递给 Auth::attempt
方法的凭据数组。然后,该方法应“查询”底层持久存储以查找与这些凭据匹配的用户。通常,此方法将使用 $credentials['username']
上的“where”条件运行查询。然后,该方法应返回 Authenticatable
的实现。此方法不应尝试进行任何密码验证或认证。
validateCredentials
方法应将给定的 $user
与 $credentials
进行比较以认证用户。例如,此方法可能应使用 Hash::check
将 $user->getAuthPassword()
的值与 $credentials['password']
的值进行比较。此方法应返回 true
或 false
,指示密码是否有效。
可认证契约
现在我们已经探讨了 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
类,因此您可以查看此类以获取实现示例。
事件
Laravel 在认证过程中会触发各种事件。您可以在 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',
],
];