社交名媛:合并具有相同电子邮件的帐户

时间:2018-11-30 01:05:20

标签: php laravel laravel-socialite

im使用socialite为用户提供使用Facebook或github登录的选项。但是当用户使用facebook登录后,然后使用github登录时,将创建2个单独的帐户。所以我的问题是,有没有办法将这两个帐户合并为一个?例如,如果已经使用facebook登录的用户使用相同的电子邮件地址登录github,则不会创建新帐户,而只会简单地登录

    <?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateUsersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->string('profile');
            $table->string('slug');
            $table->string('provider_id');
            $table->string('email')->unique();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('users');
    }
}

登录/注册码

/**
 * Redirect the user to the provider authentication page.
 *
 * @return Response
 */
public function redirectToProvider($provider)
{
    return Socialite::driver($provider)->redirect();
}

/**
 * Obtain the user information from the provider.
 *
 * @return Response
 */
public function handleProviderCallback($provider)
{
    $SocialUser = Socialite::driver($provider)->stateless()->user();

    $user = $this -> findOrCreateUser($SocialUser,$provider);

    auth()->login($user,true);


    return redirect('/');

}

protected function findOrCreateUser($SocialUser,$provider)
{
    $user = User::firstOrNew(['provider_id' => $SocialUser->id]);

        if ($user->exists) return $user;
    $user->fill([
    'name' => $SocialUser->nickname?:$SocialUser->name,
    'slug' => str_slug($SocialUser->nickname?:$SocialUser->name).'-'.uniqid(),
        'email' => $SocialUser->email,
        'avatar' => $SocialUser->avatar,
        'profile' => Hash::make('no pic'),
        'password' => Hash::make('no need for password token based'),
    // 'website' => 'add a website',
    // 'github_profile' => 'add github profile',
    'email_notifications' => 1
    ])->save();
    $user->assignRole('user');
    \Mail::to($user)->send(new Welcome($user));
    session()->flash('message','Welcome to '.config('app.name').' '.$user->name);
    return $user;
}

}

2 个答案:

答案 0 :(得分:1)

我有一个解决方案,它充分利用了此问答中的一切精神。

/**
 * @param  string $provider
 * @param  \Laravel\Socialite\Contracts\User $sUser
 * @return \App\User|false
 */
protected function findOrCreateUser($provider, $sUser)
{
    $oauthProvider = OAuthProvider::where('provider', $provider)
        ->where('provider_user_id', $sUser->id)
        ->first();

    if ($oauthProvider) {
        $oauthProvider->update([
            'access_token' => $sUser->token,
            'refresh_token' => $sUser->refreshToken ?? null,
        ]);

        return $oauthProvider->user;
    }

    $user = User::firstWhere('email', $sUser->email);

    if ($user) {
        return $this->createUser($provider, $sUser, $user);
    }

    return $this->createUser($provider, $sUser);
}

/**
 * If a User already exists for the email, skip user creation
 * and add this provider to the list of `$user->oauthProviders`.
 * @param  string $provider
 * @param  \Laravel\Socialite\Contracts\User $sUser
 * @param \App\User $user
 * @return \App\User
 */
protected function createUser($provider, $sUser, User $user = null)
{
    if (!$user) {
        $user = User::create([
            'name' => $sUser->name,
            'email' => $sUser->email,
            'email_verified_at' => now(),
        ]);
    } else if ($user->email_verified_at === null) {
        $user->email_verified_at = now();
        $user->save();
    }

    $user->oauthProviders()->create([
        'provider' => $provider,
        'provider_user_id' => $sUser->id,
        'access_token' => $sUser->token,
        'refresh_token' => $sUser->refreshToken ?? null,
    ]);

    return $user;
}

之前,它检查了User::where('email', $sUser->email)是否存在,如果是,则以“已接收电子邮件”消息拒绝该请求。

通过oauth_providers表和$user->oauthProviders关系(用户hasMany OAuthProviders),而不是每次有人使用oauth时都在users表中创建新用户,而是将oauth记录附加到现有用户{ {1}}

如果有人想要更多,我在这里修改了此仓库,使GitHub和Twitter oauth都可以使用:https://github.com/cretueusebiu/laravel-vue-spa。以OAuthController为基础。

使用上述代码,我可以通过注册表单注册用户以捕获电子邮件,然后以GitHub和Twitter身份登录,并让我的用户加上两个oauth提供商。

我的解决方案的大部分魔力来自$user = User::firstWhere('email', $sUser->email);上的第3个参数。将createUser保留为始终创建状态,然后创建一个名为addProviderToUser的新方法是否更好,还有待观察。那可能是更多的代码,但是对于单元测试也可能更简单,更友好。

出于科学原因,这也是我的oauth重定向和回调方法:

createUser

config / services.php

/**
 * Redirect the user to the provider authentication page. Twitter uses OAuth1.0a, and does not support
 * Socialite::driver($provider)->stateless(), so library `abraham/twitteroauth` is used to handle everything.
 *
 * @param  string $provider
 * @return \Illuminate\Http\RedirectResponse
 */
public function redirectToProvider($provider)
{
    if ($provider === 'twitter') {
        $tempId = Str::random(40);

        $connection = new TwitterOAuth(config('services.twitter.client_id'), config('services.twitter.client_secret'));
        $requestToken = $connection->oauth('oauth/request_token', array('oauth_callback' => config('services.twitter.callback_url').'?user='.$tempId));

        \Cache::put($tempId, $requestToken['oauth_token_secret'], 86400); // 86400 seconds = 1 day

        $url = $connection->url('oauth/authorize', array('oauth_token' => $requestToken['oauth_token']));
    } else {
        $url = Socialite::driver($provider)->stateless()->redirect()->getTargetUrl();
    }

    return [
        'url' => $url,
    ];
}

/**
 * Obtain the user information from the provider.
 *
 * @param  string $driver
 * @return \Illuminate\Http\Response
 */
public function handleProviderCallback(Request $request, $provider)
{
    if ($provider === 'twitter') {
        $connection = new TwitterOAuth(config('services.twitter.client_id'), config('services.twitter.client_secret'), $request->oauth_token, \Cache::get($request->user));
        $access_token = $connection->oauth('oauth/access_token', ['oauth_verifier' => $request->oauth_verifier]);

        $connection = new TwitterOAuth(config('services.twitter.client_id'), config('services.twitter.client_secret'), $access_token['oauth_token'], $access_token['oauth_token_secret']);
        $user = $connection->get('account/verify_credentials', ['include_email' => 'true']);

        $user->token = $access_token['oauth_token'];
    } else {
        $user = Socialite::driver($provider)->stateless()->user();
    }
    $user = $this->findOrCreateUser($provider, $user);

    $this->guard()->setToken(
        $token = $this->guard()->login($user)
    );

    return view('oauth/callback', [
        'token' => $token,
        'token_type' => 'bearer',
        'expires_in' => $this->guard()->getPayload()->get('exp') - time(),
    ]);
}

.env

'github' => [
    'client_id' => env('GITHUB_CLIENT_ID'),
    'client_secret' => env('GITHUB_CLIENT_SECRET'),
    'callback_url' => env('GITHUB_CALLBACK_URL'),
    'provider_name' => env('GITHUB_PROVIDER_NAME', 'GitHub'),
],

'twitter' => [
    'client_id' => env('TWITTER_CLIENT_ID'),
    'client_secret' => env('TWITTER_CLIENT_SECRET'),
    'callback_url' => env('TWITTER_CALLBACK_URL'),
    'provider_name' => env('TWITTER_PROVIDER_NAME', 'Twitter'),
],

您必须查看上面的示例存储库,以了解如何使用这些env变量,但要提示:查看spa.blade.php,vuex和api.php

答案 1 :(得分:0)

尝试将您的代码更改为此:

$user = User::where('email', $SocialUser->email)->first();

if (!empty($user) && in_array($SocialUser->id, $user->provider_id) ) {

    session()->flash('message','Welcome to '.config('app.name').' '.$user->name);

    return $user;

}

if (empty($user) ) {

    $user = User::create([
        'name' => $SocialUser->nickname?:$SocialUser->name,
        'slug' => str_slug($SocialUser->nickname?:$SocialUser->name).'-'.uniqid(),
        'email' => $SocialUser->email,
        'avatar' => $SocialUser->avatar,
        'profile' => Hash::make('no pic'),
        'password' => Hash::make('no need for password token based'),
        // 'website' => 'add a website',
        // 'github_profile' => 'add github profile',
       'email_notifications' => 1,
       'provider_id' => [$SocialUser->id]
    ]);

    $user->assignRole('user');

    \Mail::to($user)->send(new Welcome($user));

    session()->flash('message','Welcome to '.config('app.name').' '.$user->name);

    return $user;

}

$providers = array_push($user->provider_id, $SocialUser->id);

$user->update([
    'provider_id' => $providers
]);

session()->flash('message','Welcome to '.config('app.name').' '.$user->name);

return $user;

您最好也将其添加到您的用户模型中:

protected $casts = [
    'provider_id' => 'array'
];

我希望这对您有帮助

相关问题