身分验证

Laravel 提供了一整套身分验证相关的功能,并且当我们安装 Breeze 的时候就已经帮我们实作了大部分机能,不过为了客制或调整其中的功能,还是来了解一下运作的机制。

Auth Facade

Auth Facade 帮助快速取用验证的相关功能,包含登入登出等等。这边先介绍直接使用这些功能的方法,底下再来解说背後的机制。

登入

Auth::attempt

当使用者试图登入的时候,在请求中带上验证身分所需的栏位值,在 Laravel 中预设的就是 email , password 。

/app/Http/Requests/Auth/LoginRequest.php

public function rules()
{
    return [
        'email' => ['required', 'string', 'email'],
        'password' => ['required', 'string'],
    ];
}

这边还没介绍过资料检查( Validation )的功能,不过上面的程序码应该很好理解,email 跟 password 都是必须( required )栏位。

取得必须的栏位值之後就能用 Auth::attempt 进行检查。

/app/Http/Requests/Auth/LoginRequest.php

use Illuminate\Support\Facades\Auth;

//...

public function authenticate()
{
    $this->ensureIsNotRateLimited();

    // 如果 attempt 检查失败的话就抛出错误
    if (! Auth::attempt($this->only('email', 'password'), $this->boolean('remember'))) {
        RateLimiter::hit($this->throttleKey());

        throw ValidationException::withMessages([
            'email' => __('auth.failed'),
        ]);
    }

    RateLimiter::clear($this->throttleKey());
}

Auth::attempt 的第一个参数是要验证的资料阵列,写得直接点的话会像

Auth::attempt([
    'email' => 'your_email',
    'password' => 'your_password'
]);

第二个参数则用於指定是否要记忆这个使用者,该值为 true 的话就记忆。

Auth::attempt([
    'email' => 'your_email',
    'password' => 'your_password'
    ],
    $rememberMe
);

而记忆的方式则是在 user 的 remember_token 栏位存入 token ,之後使用者用网页请求时就会用 cookie 内的资讯来对照这个 token 确认是否让使用者快速登入。而当使用者登出的时候这个栏位就会清空,帮助防止 cookie 被劫持的安全问题

为了验证身分所必须的栏位其实只有 password ,除 password 外添加的栏位都是用来检索使用者,这些额外的栏位会应用於 where 的搜寻,只有被搜寻到的使用者会进行密码验证。

// 信箱相符且被启用(active)的用户才进行密码验证
if (Auth::attempt(['email' => $email, 'password' => $password, 'active' => 1])) {
 
}

Auth::attempt 仅用於判定登入资讯是否正确,判定後是要导向首页或是使用者原本尝试拜访的页面、产生 session id 等行为都是另外定义的。

if (Auth::attempt(['email' => $email, 'password' => $password])) {

    $request->session()->regenerate();

    return redirect()->intended(RouteServiceProvider::HOME);
}

Auth::login

那如果使用者才刚申请完帐号怎麽办呢? 如果是在已经有 User 实例的情况下,可以直接用 login 方法指定该 User 为目前登入的使用者,并更新 session 。

/app/Http/Controllers/Auth/RegisteredUserController.php

public function store(Request $request)
{
    $request->validate([
        'name' => 'required|string|max:255',
        'email' => 'required|string|email|max:255|unique:users',
        'password' => ['required', 'confirmed', Rules\Password::defaults()],
    ]);

    $user = User::create([
        'name' => $request->name,
        'email' => $request->email,
        'password' => Hash::make($request->password),
    ]);

    $user->setting()->create();

    event(new Registered($user));

    Auth::login($user);  // 指定新申请的用户为登入的 user

    return redirect(RouteServiceProvider::HOME);
}

跟 attempt 一样,可以指定是否记忆使用者

Auth::login($user,$remeberMe);

登出

Auth::logout

使用 Auth::logout() 能将更新 user session ,移除其中的登入资讯。

不过官方建议除了登出外也要完全清除 session 资讯,并且重产 csrf_token。

public function logout(Request $request)
{
    Auth::logout();

    $request->session()->invalidate();

    $request->session()->regenerateToken();

    return redirect('/');
}

auth config

Laravel 的身分验证主要有两个设定, guards 跟 providers

providers

跟身分验证有关的系统设定都在 config/auth.php 中。

首先可以找到 providers 的资料。

'providers' => [
    'users' => [
        'driver' => 'eloquent',
        'model' => App\Models\User::class,
    ],
],

providers 可以理解为发行身分证的来源,主要就是指定要去哪里取得身分相关的资料,像这边定义 users 使用 eloquent 来查询 App\Models\User 的资料,并利用查询出的资料进行身分验证。我们也可追加不同的身分发行类别。

'providers' => [
    'users' => [
        'driver' => 'eloquent',
        'model' => App\Models\User::class,
    ],
    'admins' => [
        'driver' => 'eloquent',
        'model' => App\Models\Admin::class,
    ], 
],

用在这边的 model 需要经过特殊处理,需要有对应的栏位好进行身分验证,最直接的方法就是继承框架的 User Model

<?php
  
use Illuminate\Foundation\Auth\User as Authenticatable; 

class Admin extends Authenticatable  
{
    //...
}

再打开 Illuminate\Foundation\Auth\User 可以看到也是很多介面或属性的组合,可以参考来自制想要的 User Model ,比如说不想要信箱验证功能的话,就不要引入 MustVerifyEmail 属性。

/vendor/laravel/framework/src/Illuminate/Foundation/Auth/User.php

<?php

namespace Illuminate\Foundation\Auth;

use Illuminate\Auth\Authenticatable;
use Illuminate\Auth\MustVerifyEmail;
use Illuminate\Auth\Passwords\CanResetPassword;
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Auth\Access\Authorizable;

class User extends Model implements
    AuthenticatableContract,
    AuthorizableContract,
    CanResetPasswordContract
{
    use Authenticatable, Authorizable, CanResetPassword, MustVerifyEmail;
}

guards

guards 用於定义不同的"关口",各个关口会察看不同的身分证,或是用不同的方法来检查身分证。

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

像这边定义了 web 这个关口,只检查 providers 中的 users 身分证,并且用 session 的方式进行检查。

检查的方式有许多种,除了 session 外 Laravel 预设的套件中有 passport 跟 sanctum ,或是第三方套件的 jwt。

可以定义多个关口

'guards' => [
    'web' => [
        'driver' => 'session',
        'provider' => 'users',
    ],
    'admin' => [
        'driver' => 'session',
        'provider' => 'admins',
    ],
    'api' => [
        'driver' => 'token',
        'provider' => 'users',
    ],      
],

当要进行身分检查时,可以先指定要用哪个 guard 进行检查,如果没有指定就是使用 config/auth.php 设定的 default 值。

// 在 Admin 的资料中找的到的话才能验证成功
Auth::guard('admin')->login($user);
Auth::guard('admin')->attempt($crednetials);

auth middleware

我们可以在路由中加上 auth 这个中介层,检查使用者的身分,如果身分符合的话才放行。

/routes/auth.php

Route::post('/logout', [AuthenticatedSessionController::class, 'destroy'])
                ->middleware('auth')
                ->name('logout');

auth 只是个快捷键,在 app/Http/Kernel.php 中定义了 auth 对应的中介层类别。

app/Http/Kernel.php

protected $routeMiddleware = [
    'auth' => \App\Http\Middleware\Authenticate::class,
    //...
];

再仔细看看这个 Authenticate 类别,也只是继承了框架底层的类别,详细进行身分验证的检查都在 Illuminate\Auth\Middleware\Authenticate 中。

在专案的 app 目录底下的 Authenticate 目前只用来指定当身分检查失败时要导向哪个页面。

/app/Http/Middleware/Authenticate.php

<?php

namespace App\Http\Middleware;

use Illuminate\Auth\Middleware\Authenticate as Middleware;

class Authenticate extends Middleware
{
    protected function redirectTo($request)
    {
        if (! $request->expectsJson()) {
            return route('login'); // 身分验证失败就导向登入页面
        }
    }
}

auth:guard

当加入 auth 中介层时如果没有额外指定,就是用预设的 guard 进行检查。

要指定不同的 guard 话,加上 : 後指定

Route::get('/flights', function () {
    // 通过 admin 检查的用户才能进入路由
})->middleware('auth:admin');

验证方法

Laravel 预设的身分检查是用基础的 session-cookie 方式,如果想要改成其他方法的话可以利用套件。

这边简单介绍几个可以用的套件,各自的安装跟使用又都是大长篇就先不细说。

Passport

Laravel 预设包含的套件之一,可以用来进行 Outh2 验证。

Sanctum

Laravel 预设包含的套件之一,以简易的 token 进行验证,可以应用於 SPA 跟行动装置的 API 验证。

jwt-auth

顾名思义登入後就产出 jwt ,让客户端存好後使用。

laravel-keyable

帮 Model 产出 API key 并用来验证,可以用於 S2S 的 API 验证。


<<:  Day 24: Lab-Distributed Machine Learning with Google Cloud ML

>>:  [Day 24] 阿嬷都看得懂的响应式网页设计在干嘛

Day13 - Tree((超出时间也太多了吧QAQ

大家好,我是长风青云。我真的没想到今天我会讲这麽久的影片QAQ 然後我可爱的(?)学弟居然在最後密我...

DAY05 - [CSS] 三角型,来个推荐标签吧!

今日文章目录 > - 三角型使用情境 > - 用CSS画三角型 > - 参考资料...

DAY5 - Side Project 主题:90天原子习惯挑战

荀子劝学篇中有一段是这样的: 「积土成山,风雨兴焉;积水成渊,蛟龙生焉;.....。故不积蹞步,无以...

IT铁人DAY 27-Visitor 访问者模式

  今天要认识的Design Pattern我觉得比较难,但我会尽量以简单的方法让大家了解 Visi...

从 IT 技术面细说 Search Console 的 27 组数字 KPI (3) 点击 (2) 网页搜寻

上一篇提到可以从 Search Console 看到 6 种不同的流量来源,而 SC 提供的概要是用...