Laravel 用戶多字段認證優雅解決方案

解決方案:

  • 登陸字段不超過兩個的(簡單的解決方案)
  • 登陸字段大於或等於三個的(相對複雜一些)

登陸字段不超過兩個的

我在網上看到一種相對簡單解決方案,可是不能解決全部兩個字段的驗證:php

filter_var($request->input('login'), FILTER_VALIDATE_EMAIL) ? 'email' : 'name'

過濾請求中的表單內容,實現區分 username。弊端顯而易見,若是另外一個不是 email 就抓瞎了……,下面是另外一種通用的解決方案:laravel

在 LoginController 中重寫 login 方法web

public function login(Requests $request) {
    //假設字段是 email
    if ($this->guard()->attempt(['username' =>$request->only('email'), 'password' => $request->only('password')]))) {
        return $this->sendLoginResponse($request);
    }

    //假設字段是 mobile
    if ($this->guard()->attempt(['username' =>$request->only('mobile'), 'password' => $request->only('password')])) {
        return $this->sendLoginResponse($request);
    }

    //假設字段是 username
    if ($this->guard()->attempt(['username' =>$request->only('username'), 'password' => $request->only('password')])) {
        return $this->sendLoginResponse($request);
    }

    return $this->sendFailedLoginResponse($request);
}

能夠看到雖然能解決問題,可是顯然有悖於 Laravel 的優雅風格,賣了這麼多關子,下面跟你們分享一下個人解決方案。api

登陸字段大於或等於三個的(相對複雜一些)

圖片描述

爲了方便理解我畫了個大體的流程,只畫了我認爲重要的部分數組

首先須要本身實現一個 Illuminate\Contracts\Auth\UserProvider 的實現,具體能夠參考 添加自定義用戶提供器 可是我喜歡偷懶,就直接繼承了 EloquentUserProvider,並重寫了 retrieveByCredentials 方法:session

public function retrieveByCredentials(array $credentials)
{
    if (empty($credentials)) {
        return;
    }

    $query = $this->createModel()->newQuery();

    foreach ($credentials as $key => $value) {
        if (! Str::contains($key, 'password')) {
            $query->orWhere($key, $value);
        }
    }

    return $query->first();
}

注意: 全文關鍵的點就是框架自帶的 $query->where($key, $value);,也就是說字段之間是 and 的關係;那麼將 $query->where($key, $value); 改成 $query->orWhere($key, $value); 就能夠了!app

緊接着須要註冊自定義的 UserProvider:框架

class AuthServiceProvider extends ServiceProvider
{
    /**
     * 註冊任何應用認證/受權服務。
     *
     * @return void
     */
    public function boot()
    {
        $this->registerPolicies();

        Auth::provider('custom', function ($app, array $config) {
            // 返回 Illuminate\Contracts\Auth\UserProvider 實例...

            return new CustomUserProvider(new BcryptHasher(), config('auth.providers.custom.model'));
        });
    }
}

最後咱們修改一下 auth.php 的配置就搞定了:ide

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

     'custom' => [
         'driver' => 'custom',
         'model' => App\Models\User::class,
     ],
],

web 數組的 provider 修改成前面註冊的那個 customthis

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

    'api' => [
        'driver' => 'passport',
        'provider' => 'users',
    ],
],

最後看一下 LoginController 的代碼:

public function login(LoginRequest $request)
{
    $username = $request->get('username');
    $result = $this->guard()->attempt([
        'username' => $username,
        'email' => $username,
        'mobile' => $username,
        'password' => $request->get('password')]);

    if ($result) {
        return $this->sendLoginResponse($request);
    }

    $this->incrementLoginAttempts($request);
    return $this->sendFailedLoginResponse($request);
}

如今哪怕你有在多個字段都妥妥的……??

相關文章
相關標籤/搜索