在新的 Laravel 应用上运行
php artisan make:auth
和php artisan migrate
命令。就可以构建好整个认证系统。
Laravel 中实现用户认证非常简单,几乎所有的东西都为你配置好了。其配置文件位于 config/auth.php
,包含了相关的选项配置。
其核心由 Laravel 的认证组件「看守器」和「提供器」组成。
看守器定义了该如何认证每个请求中的用户。例如,Laravel 自带的 session 看守器会使用 session 存储和 cookies 来维护状态。
提供器定义了该何如从持久化的存储数据中检索用户。Laravel 自带支持使用 Eloquent 和数据库查询构造器来检索用户。当然,你也可以根据需要自定义其他提供器。
不过对于大多数应用而言,可能永远不需要修改默认身份认证配置。
默认情况下,Laravel 在 app 目录中包含了一个 Eloquent 模型 App\User
。这个模型和默认的 Eloquent 认证驱动一起使用。如果不打算使用 Eloquent,也可以使用 Laravel 查询构造器的 database 认证驱动。
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');
}
看守器 web 实例是这样的:
SessionGuard {#436 ▼
#name: "web"
#lastAttempted: null
#viaRemember: false
#session: Store {#378 ▶}
#cookie: CookieJar {#366 ▶}
#request: Request {#55 ▶}
#events: Dispatcher {#35 ▶}
#loggedOut: false
#recallAttempted: false
#user: null
#provider: EloquentUserProvider {#431 ▶}
}
看守器 api 实例是这样的:
TokenGuard {#431 ▼
#request: Request {#55 ▶}
#inputKey: "api_token"
#storageKey: "api_token"
#user: null
#provider: EloquentUserProvider {#432 ▶}
}
可以通过 Auth
facade 来访问认证的用户:
use Illuminate\Support\Facades\Auth;
// 获取当前已认证的用户
$user = Auth::user();
// 获取当前已认证的用户 ID
$id = Auth::id();
或者,还可以通过 Illuminate\Http\Request
实例来访问已认证的用户。类型提示的类会被自动注入到控制器方法中。
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class ProfileController extends Controller
{
public function update(Request $request)
{
// $request->user() 返回已认证的用户实例
}
}
可以使用 Auth
facade 的 check
方法来检查用户是否登录,如果已认证,将会返回 true
。
use Illuminate\Support\Facades\Auth;
if (Auth::check()) {
// 用户已登录
}
即使可以使用
check
方法来确定用户是否被认证,通常,在允许用户访问某些路由或控制器之前,还是会使用中间件来验证用户是否进行身份验证。
路由中间件只允许通过认证的用户访问指定的路由。Laravel 自带了在 Illuminate\Auth\Middleware\Authenticate
中定义的 auth
中间件。由于这个中间件已经在 HTTP 内核中注册,所以只需要将中间件附加到路由定义中:
Route::get('profile', function () {
// 只有认证过的用户可以
})->middleware('auth');
当然,如果是控制器,则可以在构造函数中调用:
public function __construct()
{
$this->middleware('auth');
}
当 auth
中间件检测到一个未认证的用户时,会返回一个 401 的 JSON 响应,或者如果检测到不是 AJAX 请求,会重定向到名为 login
的路由。
可以在 app/Exceptions/Handler.php
中重写 unauthenticated
方法来进行修改:
use Illuminate\Auth\AuthenticationException;
protected function unauthenticated($request, AuthenticationException $exception)
{
return $request->expectsJson()
? response()->json(['message' => $exception->getMessage()], 401)
: redirect()->guest(route('login'));
}
将 auth
中间件添加到路由时,还需要指定使用哪个看守器来认证用户。指定的看守器对应配置文件 auth.php
中 guards
数组的某个键:
public function __construct()
{
$this->middleware('auth:api');
}
如果不指定,默认使用第一个。
Laravel 内置的控制器 LoginController
已经包含了 Illuminate\Foundation\Auth\ThrottlesLogins
trait。默认情况下,如果用户在进行几次尝试后仍不能提供正确的凭证,该用户将在一分钟内无法进行登录。这个限制基于用户的用户名、邮箱地址和 IP 地址。
可以使用 Auth
facade 的 extend
方法来定义自己的身份验证提供器。需要在服务提供器中调用这个提供器,由于 Laravel 已经配备了 AuthServiceProvider
,我们可以把代码放在这个提供器内:
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
{
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',
],
],
如果没有使用传统的关系数据库来存储用户信息,则需要使用自己的用户认证提供器来扩展。我们使用 Auth
facade 上的 provider
方法自定义用户提供器:
namespace App\Providers;
use Illuminate\Support\Facades\Auth;
use App\Extensions\RiakUserProvider;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
class AuthServiceProvider extends ServiceProvider
{
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',
],
],
Laravel 在认证过程中引发了各种各样的事件。可以在 EventServiceProvider
中对这些事件做监听:
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',
],
];