Laravel 用户认证系统(一)

老牛浏览 454评论 0发表于

1. 简介

在新的 Laravel 应用上运行 php artisan make:authphp artisan migrate 命令。就可以构建好整个认证系统。

Laravel 中实现用户认证非常简单,几乎所有的东西都为你配置好了。其配置文件位于 config/auth.php,包含了相关的选项配置。

其核心由 Laravel 的认证组件「看守器」和「提供器」组成。

看守器定义了该如何认证每个请求中的用户。例如,Laravel 自带的 session 看守器会使用 session 存储和 cookies 来维护状态。

提供器定义了该何如从持久化的存储数据中检索用户。Laravel 自带支持使用 Eloquent 和数据库查询构造器来检索用户。当然,你也可以根据需要自定义其他提供器。

不过对于大多数应用而言,可能永远不需要修改默认身份认证配置。

2. 数据库注意事项

默认情况下,Laravel 在 app 目录中包含了一个 Eloquent 模型 App\User。这个模型和默认的 Eloquent 认证驱动一起使用。如果不打算使用 Eloquent,也可以使用 Laravel 查询构造器的 database 认证驱动。

自定义用户名

Laravel 默认使用 email 字段来认证。如果要使用其他字段认证,可以在 LoginController 中定义一个 username 方法:

php
public function username()
{
    return 'username';
}

自定义看守器

还可以自定义用于认证和注册用户的「看守器」。要实现这一功能,需要在 LoginControllerRegisterControllerResetPasswordController 中定义 guard 方法。该方法需要返回一个看守器实例:

php
use Illuminate\Support\Facades\Auth;

protected function guard()
{
    return Auth::guard('guard-name');
}

看守器 web 实例是这样的:

php
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 实例是这样的:

php
TokenGuard {#431 ▼
  #request: Request {#55 ▶}
  #inputKey: "api_token"
  #storageKey: "api_token"
  #user: null
  #provider: EloquentUserProvider {#432 ▶}
}

检索认证用户

可以通过 Auth facade 来访问认证的用户:

php
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
{
    public function update(Request $request)
    {
        // $request->user() 返回已认证的用户实例
    }
}

确定当前用户是否认证

可以使用 Auth facade 的 check 方法来检查用户是否登录,如果已认证,将会返回 true

php
use Illuminate\Support\Facades\Auth;

if (Auth::check()) {
    // 用户已登录
}

即使可以使用 check 方法来确定用户是否被认证,通常,在允许用户访问某些路由或控制器之前,还是会使用中间件来验证用户是否进行身份验证。

保护路由

路由中间件只允许通过认证的用户访问指定的路由。Laravel 自带了在 Illuminate\Auth\Middleware\Authenticate 中定义的 auth 中间件。由于这个中间件已经在 HTTP 内核中注册,所以只需要将中间件附加到路由定义中:

php
Route::get('profile', function () {
    // 只有认证过的用户可以
})->middleware('auth');

当然,如果是控制器,则可以在构造函数中调用:

php
public function __construct()
{
    $this->middleware('auth');
}

重定向未认证的用户

auth 中间件检测到一个未认证的用户时,会返回一个 401 的 JSON 响应,或者如果检测到不是 AJAX 请求,会重定向到名为 login 的路由。

可以在 app/Exceptions/Handler.php 中重写 unauthenticated 方法来进行修改:

php
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.phpguards 数组的某个键:

php
public function __construct()
{
    $this->middleware('auth:api');
}

如果不指定,默认使用第一个。

登录限制

Laravel 内置的控制器 LoginController 已经包含了 Illuminate\Foundation\Auth\ThrottlesLogins trait。默认情况下,如果用户在进行几次尝试后仍不能提供正确的凭证,该用户将在一分钟内无法进行登录。这个限制基于用户的用户名、邮箱地址和 IP 地址。

3. 增加自定义的看守器

可以使用 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
{
    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 配置中使用了。

php
'guards' => [
    'api' => [
        'driver' => 'jwt',
        'provider' => 'users',
    ],
],

4. 添加自定义用户提供器

如果没有使用传统的关系数据库来存储用户信息,则需要使用自己的用户认证提供器来扩展。我们使用 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
{
    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

php
'providers' => [
    'users' => [
        'driver' => 'riak',
    ],
],

最后,在 guards 配置中使用这个提供器:

php
'guards' => [
    'web' => [
        'driver' => 'session',
        'provider' => 'users',
    ],
],

5. 事件

Laravel 在认证过程中引发了各种各样的事件。可以在 EventServiceProvider 中对这些事件做监听:

php
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',
    ],
];
点赞
收藏
暂无评论,快来发表评论吧~