如何创建Laravel 5.1自定义认证驱动程序?

10

我正在使用 Laravel Socialite 进行身份验证登录。现在我已经可以保存来自社交账号的用户数据,但现在我面临如何从Gmail、GitHub等账号进行用户认证的问题。

经过一些研究,我了解到我需要创建自定义认证。我搜索了一下,但所有结果都是针对 Laravel 4.1 的主题。如果有人处理过此类问题,请提供答案。

我已经阅读了以下内容,但仍然不知道该如何实施?

http://laravel.com/docs/5.1/authentication#social-authentication

http://laravel.com/docs/5.1/providers

http://laravel-recipes.com/recipes/115/using-your-own-authentication-driver

http://laravel.io/forum/11-04-2014-laravel-5-how-do-i-create-a-custom-auth-in-laravel-5

更新:

public function handleProviderCallback() {
    $user = Socialite::with('github')->user();
    $email=$user->email;
    $user_id=$user->id;

    //$authUser = User::where('user_id',$user_id)->where('email', $email)->first();
    $authUser = $this->findOrCreateUser($user);

    if(Auth::login($authUser, true)) {
        return Redirect::to('user/UserDashboard');
    }   
}

private function findOrCreateUser($user) {
    if ($authUser = User::where('user_id',$user->id)->first()) {
        return $authUser;
    }

    return User::create([
        'user_id' => $user->id,
        'name' => $user->nickname,
        'email' => $user->email,
        'avatar' => $user->avatar
    ]);
}

为什么您认为需要创建自定义身份验证?Socialite具有Google和GitHub的驱动程序。 - Martin Bean
@MartinBean 我是 Laravel 的新手。我能够保存数据,但现在我遇到了一个问题,即如何进行身份验证。 - scott
2个回答

17

在Laravel 5.1中添加自定义认证驱动程序

本答案最适用于Laravel 5.1。如果您使用其他版本,请小心。请注意,我认为这是Laravel中相当高级的水平,因此如果您不完全了解自己在做什么,可能会导致应用程序崩溃。该解决方案并非端到端正确。这只是一个通用指南,告诉您需要做什么才能使其正常工作。

提示:有关此主题的Laravel文档在此处

提示2:您提到的最后一个链接在我看来非常有用。阅读该链接后,我学到了所有这些知识。

http://laravel.io/forum/11-04-2014-laravel-5-how-do-i-create-a-custom-auth-in-laravel-5


在开始之前,我想先描述一下登录流程,这将有助于您了解该过程。Laravel使用一个driver来连接数据库以获取您的记录。 Laravel预装了两个驱动程序 - eloquentdatabase。我们想创建第三个驱动程序,以便我们可以根据需要进行自定义。
您的vendor文件夹中的Illuminate\Auth\Guard是主要文件,其中包含用户登录和注销的代码。此文件主要使用两个Contracts(或接口),我们需要覆盖这些接口才能使我们的驱动程序正常工作。从Laravel的文档中阅读以下内容:
“Illuminate\Contracts\Auth\UserProvider实现仅负责从持久存储系统(例如MySQL,Riak等)中提取Illuminate\Contracts\Auth\Authenticatable实现。这两个接口允许Laravel身份验证机制继续运行,而不管用户数据存储在哪里或使用什么类型的类来表示它。”
因此,我们的驱动程序需要实现Illuminate\Contracts\Auth\UserProviderIlluminate\Contracts\Auth\Authenticatable,并告诉Laravel使用这些实现而不是默认值。
好的,让我们开始吧。 步骤1: 为您的驱动程序选择一个名称。我将我的命名为socialite。然后在您的config/auth.php文件中,将driver的名称更改为socialite。通过这样做,我们刚告诉Laravel使用此驱动程序进行身份验证,而不是默认的eloquent步骤2: 在您的app/Provider/AuthServiceProvider文件的boot()方法中添加以下行:
Auth::extend('socialite', function($app) {
    $provider = new SocialiteUserProvider();
    return new AuthService($provider, App::make('session.store'));
});

我们在这里所做的是:
  • 我们首先使用 Auth 门面来定义 socialite 驱动程序。
  • SocialiteUserProviderUserProvider 的一个实现。
  • AuthService 是我对 Guard 类的扩展。该类构造函数的第二个参数是会话,Laravel 使用它来获取和设置会话。
  • 因此,我们基本上告诉 Laravel 使用我们自己的 Guard 类实现而不是默认的实现。

步骤 3: 创建 SocialiteUserProvider。如果您阅读 Laravel 的文档,您将理解每个方法应该返回什么。我已经创建了第一个方法作为示例。正如您所看到的,我使用我的 UserService 类来获取结果。您可以以任何想要的方式获取您自己的结果。然后我创建了一个 User 对象。这个 User 类实现了 Illuminate\Contracts\Auth\Authenticatable 契约。

<?php
namespace App\Extensions;

use App\User;
use App\Services\UserService;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Auth\UserProvider;

class SocialiteUserProvider implements UserProvider
{
    private $userService;

    public function __construct(UserService $userService)
    {
        $this->userService = $userService;
    }

    public function retrieveById($identifier)
    {
        $result = $this->userService->getUserByEmail($identifier);
        if(count($result) === 0)
        {
            $user = null;
        }
        else
        {
            $user = new User($result[0]);
        }

        return $user;
    }

    public function retrieveByToken($identifier, $token)
    {
        // Implement your own.
    }

    public function updateRememberToken(Authenticatable $user, $token)
    {
        // Implement your own.
    }

    public function retrieveByCredentials(array $credentials)
    {
        // Implement your own.
    }

    public function validateCredentials(Authenticatable $user, array $credentials)
    {
        // Implement your own.
    }
}

步骤4: 创建一个实现了Authenticatable接口的User类。这个类必须实现该接口,因为Guard类将使用此类来获取值。
<?php
namespace App;

use Illuminate\Contracts\Auth\Authenticatable;

class User implements Authenticatable
{
    protected $primaryKey = 'userEmail';
    protected $attributes = [];

    public function __construct(array $attributes)
    {
        $this->attributes = $attributes;
    }

    public function getUserAttributes()
    {
        return $this->attributes;
    }

    public function getAuthIdentifier()
    {
        return $this->attributes[$this->primaryKey];
    }

    public function getAuthPassword()
    {
        // Implement your own.
    }

    public function getRememberToken()
    {
        // Implement your own.
    }

    public function setRememberToken($value)
    {
        // Implement your own.
    }

    public function getRememberTokenName()
    {
        // Implement your own.
    }
}

步骤5: 最后创建AuthService类,调用Guard方法。这是我的实现方式,你可以根据自己的需要编写自己的实现方式。我们在这里扩展了Guard类来实现两个新函数,它们都很容易理解。
<?php
namespace App\Services;

use Illuminate\Auth\Guard;

class AuthService extends Guard
{
    public function signin($email)
    {
        $credentials = array('email' => $email);
        $this->fireAttemptEvent($credentials, false, true);
        $this->lastAttempted = $user = $this->provider->retrieveById($email);

        if($user !== null)
        {
            $this->login($user, false);
            return true;
        }
        else
        {
            return false;
        }
    }

    public function signout()
    {
        $this->clearUserDataFromStorage();

        if(isset($this->events))
        {
            $this->events->fire('auth.logout', [$this->user()]);
        }

        $this->user = null;
        $this->loggedOut = true;
    }
}

第六步:奖励步骤 为了完整回答问题,我还将解释UserService类所期望的结构。首先让我们了解一下这个类的作用。在上面的步骤中,我们创建了一切,让laravel知道如何使用我们的身份验证驱动程序,而不是他们的。但我们仍然没有告诉laravel如何获取数据。我们只告诉laravel,如果您调用userService->getUserByEmail($email)方法,您将获得您的数据。因此,现在我们只需实现此函数即可。

例如1:您正在使用Eloquent

public function getUserByEmail($email)
{
    return UserModel::where('email', $email)->get();
}

例如2:您正在使用Fluent

public function getUserByEmail($email)
{
    return DB::table('myusertable')->where('email', '=', $email)->get();
}

更新:2016年6月19日

感谢@skittles指出我没有清楚地显示文件应该放在哪里。所有的文件都应按照给定的命名空间放置。例如,如果命名空间是App\Extensions,类名是SocialiteUserProvider,那么文件的位置就是App\Extensions\SocialiteUserProvider.php。在laravel中,App目录是app文件夹。


请问您能展示一下您的UserService类吗?这是使您的答案完整所缺少的唯一部分。 - Last Templar
@Skittles 感谢您的建议。不过,您能否更具体地指出哪些地方需要进行更改呢?根据PSR4标准,命名空间指示了文件所在的位置。这也是Laravel项目结构的默认设置,因此应该很容易理解。如果您能指出需要更多信息的地方,我会考虑进行更改。 - Rash
1
@Skittles,你说得很有道理。我已经在我的答案底部更新了一个更新。PSR4是PHP编码规范,现在人们正在将其应用于他们的代码中。这包括文件名应该是什么,放置在哪里,使用空格而不是制表符等其他事项。 - Rash
@Rash - 谢谢你,Rash! - Skittles
这对于 Laravel 5.4 也是适用的吗?我尝试了很多次,但这种方法在 Laravel 5.4 上不起作用。 - Manjeet Singh Chauhan
显示剩余3条评论

0

@nathanmac.it 登录后会显示空白页面。 - scott

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接