Laravel中的Oauth认证:用Laravel Socialite进行社交登录教程

764 阅读13分钟

认证在软件工程中的重要性怎么强调都不为过。这篇文章讨论了与Laravel有关的话题.它简要地描述了OAuth和它在Laravel中的实现。 下面是你将学到的一些东西:

  • 什么是OAuth?
  • OAuth 1.0与OAuth 2.0.
  • 在Laravel中实现OAuth服务器(Laravel Passport).
  • 什么是社会化登录.
  • 什么是Laravel Socialite.
  • 如何用Laravel Socialite实现社会化登录.

简介

有一个普遍的误解,认为OAuth是一个认证协议。虽然OAuth不提供认证,但它确实提供了授权。认证是通过使用密码和用户名,生物识别技术和一次性引脚来验证用户的身份。 在系统安全方面,授权是给予用户访问特定资源或功能的权限。 OAuth被广泛用于各种应用,包括提供用户认证机制。这促使众多开发者和API供应商错误地认为OAuth本身就是一个确认协议,并错误地将其作为确认协议来利用。

一个完整的认证协议可能会告诉你一些关于用户的属性,如唯一的标识符、电子邮件地址,以及该用户是否存在于应用程序中,但OAuth却没有这样做。它既不关心用户,也不关心他们的属性或存在;它只是要求一个令牌,得到令牌,并使用它来请求一些资源。例如,你可以告诉Facebook,Hashnode.com可以访问你的个人资料或在你的时间轴上发布更新信息,而不必给Hashnode你的Facebook密码。这在很大程度上降低了风险,因此,如果Hashnode出现漏洞,你的Facebook密码仍然是安全的。

什么是OAuth?

OAuth指的是开放授权。OAuth是一个委托访问的开放标准,通常被用作网络客户允许网站或应用程序获取他们在其他网站上的数据而不给他们密码的一种方式。

它是一种验证惯例,允许你认可一个应用程序代表你与另一个应用程序进行互动,而不需要泄露你的密码。

它既不是一个API,也不是一个服务,而是一个任何人都可以实施的开放标准,应用程序可以利用它来为客户应用程序提供 "安全的指定访问"。OAuth通过HTTPS工作,用访问令牌而不是凭证授权小工具、API、服务器和应用程序。

OAuth是如何工作的?

OAuth从根本上说,授权服务器在资源所有者的认可下,将访问令牌分配给第三方客户,反过来,客户使用这些令牌访问由资源服务器提供的安全数据。

让我们来看看试图在GitHub(消费者应用程序)上登录谷歌(服务提供商)的场景。首先,GitHub向谷歌寻求许可,要求提供客户端密钥和密码。这有助于谷歌验证GitHub是否可以访问请求的资源。一旦验证通过,用户就会被重定向到谷歌,决定是否登录。如果用户登录,他或她就允许GitHub使用由服务提供商(谷歌)存储的他或她的资源。此后,GitHub(消费者应用程序)会获得一个访问令牌,用于向谷歌(服务提供商)的受保护资源发出请求。令牌带有对API的访问授权。这些授权被称为范围,每个令牌对每个API都有一个授权范围。GitHub只能在范围中定义的程度上访问谷歌。

OAuth 1.0与OAuth 2.0的对比

近来,当我们提到OAuth时,我们会自动参考OAuth 2.0,因为OAuth 1.0很早就贬值了。OAuth 1.0是在2010年4月发布的,而OAuth 2.0是在2012年10月发布的。

OAuth 2.0是对OAuth 1.0的完全修改,不排除任何东西,只分享一般目标和用户体验。OAuth 2.0与OAuth 1.0或1.1并不向后兼容,应该被认为是一个完全现代的惯例。这意味着,如果你用OAuth 2.0构建一个应用程序,它属于2.0,因为1.0已经贬值了。

密码学

OAuth 1.0需要密码学的实现和密码学的互操作性。虽然它是安全的,但对于众多的开发者来说,它是一个挑战。

建立一个安全的OAuth解决方案并不是一个简单的挑战。大型企业加入了OAuth标准机构,并在众多方面对其产生影响。虽然OAuth 2.0比OAuth 1.0及其加密基础更容易实现,但新版本在安全层面上包含了许多妥协。

清晰的角色分离

OAuth2可以被描述为一个带有授权服务器的资源服务器(API)。这意味着,在处理授权请求的服务器和根据授权请求的响应产生访问控制选择的服务器之间有明确的分工。这种分离允许对更多的适应性使用案例的支持。

例如,谷歌的OAuth 2.0实现使用一个位于 "accounts.google.com "的服务器来处理授权请求,但在向Google+ API发出请求时使用 "www.gooogleapis.com"。

更好地支持非基于浏览器的应用程序

这往往是那些不基于浏览器的客户端应用程序对OAuth的主要反馈。例如,在OAuth 1.0中,桌面应用程序或多功能手机应用程序必须与用户协调,打开他们的浏览器以实现所需的好处,验证服务,并将好处中的令牌复制到应用程序中。这里的限制是用户体验。有了OAuth 2.0,应用程序就有了其他的方式来诱导客户端的授权。这是OAuth2相对于OAuth1的主要优势之一。然而,为了方便和容易而滥用这种流动,会导致OAuth2的不安全实现。

承载令牌与访问令牌

在OAuth1中,访问令牌有两个部分,一个是公共的,一个是私人的字符串。进入OAuth 2.0 API的最常见的方式是采用 "承载令牌",它作为API请求的认证,在HTTP "授权 "头中发送。这是一种更容易的API请求方式,因为它不需要对每个请求进行加密签名。

其优点是,它不需要复杂的库来提出请求,对客户和服务器来说都更容易实现。缺点是,如果其他应用程序能够获得Bearer令牌的访问权,就没有什么可以阻止他们使用该令牌。

因此,这是对OAuth2的一个常见批评。然而,如果应用程序适当地保护他们控制下的访问令牌,这不是一个问题,尽管从技术上来说,它的安全性较低。如果你的服务需要一个更安全的方法,你可以使用不同的访问令牌类型,可能满足你的安全要求。

OAuth1和OAuth2哪个好?

所有这些陈述的因素似乎表明,OAuth2优于OAuth1,OAuth1已经过时了。事实并非如此。最近开发的授权系统利用OAuth1的情况异常少见。

仍然完全支持OAuth 1.0的主要组织是Twitter;他们把他们的版本称为OAuth1.0a。然而,就安全性而言,OAuth1仍然是合理的,而且可能比OAuth2更安全,因为它在基于TLS的预防措施之上提供了额外的安全性,并在潜在的破坏性流量中建立了障碍。

使用OAuth1的现有应用程序可能不需要升级到OAuth2。依赖于服务器到服务器授权的新系统可能会使用OAuth1以获得额外的安全性。然而,从关注点的分离,非浏览器的支持和发展来看,用例似乎是有利的,应该利用OAuth2。

在Laravel中实现OAuth服务器

Laravel Passport可以在更短的时间内为Laravel应用程序提供完整的OAuth2服务器实施。从头开始开发一个OAuth2服务器可能是繁琐和耗时的,但Laravel Passport是一个用于Laravel应用程序的本地OAuth2服务器。Laravel Passport包体现了路由, 中间件, 和数据库迁移,以开发一个授权服务器,将返回访问令牌,给予服务器资源的访问权限。它使用联盟的OAuth2服务器包作为依赖,并有一个直接的,易于学习和易于实施的语言结构。

什么是社会化登录?

社会化登录,也叫社会化签到或社会化登录,允许用户利用他们在各种社会服务提供者的现有账户,在客户端应用程序上一键登录和注册。

它是一种单点登录(SSO)的进步,允许用户通过社交媒体网站连接,在不同的应用程序上验证自己,而不是在每个网站上提供一个单独的ID和密码。

从本质上讲,它使用社交网站的数据来改善第三方应用程序的登录。它旨在简化登录和注册体验,为强制性的账户创建提供一个方便的替代方案。这基本上意味着,用户应该能够用他们的任何社交账户进行注册或登录。

这对于需要获得用户资料数据的第三方应用程序来说是有利的,例如资料图片或电子邮件地址。然而,这也是一个缺点,因为它不被认为是一个安全的认证方法,不应该被用于任何形式的敏感信息的网站的认证。

Laravel的社会化登录(Laravel Socialite)

Laravel还提供了一个基本的和有用的方法来验证与OAuth供应商利用Laravel Socialite。Socialite目前支持与Facebook, Twitter, LinkedIn, Google, GitHub, GitLab, 和Bitbucket的认证.

如何用Laravel Socialite实现Social Login?

在本指南中,我将使用Laravel Socialite来实现谷歌,Facebook和GitHub的社交登录。这个主题将分为两个小节,如下所示。

创建一个认证UI支架

第一步: 创建一个新的Laravel项目

你可以通过Composer命令或Laravel安装程序创建一个新的Laravel项目:

laravel new project_name   
or
composer create-project laravel/laravel project_name

第2步: 连接到你的数据库

这里有一篇我写的文章, 解释了如何将Laravel应用连接到MySQL数据库. 如果你有一个不同的数据库, 请确保适当地连接它.

第3步: 安装Laravel/UI

composer require laravel/ui

第4步 : 创建一个Bootstrap AUTH支架

一个Bootstrap认证脚手架为注册和登录提供了一个UI和基本认证。你可以用Artisan 命令来安装它。

php artisan ui bootstrap --auth

第5步:安装NPM包

npm install

第6步:运行NPM环境

npm run dev

第7步:更新登录页面

resources\views\auth\login.blade.php ,为用户提供用社交账户登录的按钮。

注意,我们把URL路由放在登录按钮中。

@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">{{ __('Login') }}</div>
<div class="card-body">
 <form method="POST" action="{{ route('login') }}">
     @csrf
    <div class="form-group row">
    <div class="col-md-6 offset-md-3">
      <a href="{{route('login.google')}}" class="btn btn-danger btn-block">Login with Google</a>
      <a href="{{route('login.facebook')}}" class="btn btn-primary btn-block">Login with Facebook</a>
      <a href="{{route('login.github')}}" class="btn btn-dark btn-block">Login with Github</a>
   </div>
   </div>   
</form>
</div>
</div>
</div>
</div>
</div>
@endsection

第8步: 运行应用程序

你可以通过以下方式为Laravel应用程序提供服务Artisan Command:

php artisan serve

第9步: 在你的浏览器上点击这个URL

http://localhost:8000/login

你应该可以看到登录页面:

Login View

用Socialite创建社会化登录

第1步: 安装Laravel Socialite包

使用Composer包管理器将该包添加到你的项目的依赖项中:

composer require laravel/socialite

第2步: 设置路由

由于我们正在处理视图, 我们的路由将在routes\web.php.

要使用OAuth服务提供者认证用户,你将需要两条路由:一条用于将用户重定向到OAuth服务提供者,另一条用于在认证后接收服务提供者的回调。

//Google
Route::get('/login/google', [App\Http\Controllers\Auth\LoginController::class, 'redirectToGoogle'])->name('login.google');
Route::get('/login/google/callback', [App\Http\Controllers\Auth\LoginController::class, 'handleGoogleCallback']);
//Facebook
Route::get('/login/facebook', [App\Http\Controllers\Auth\LoginController::class, 'redirectToFacebook'])->name('login.facebook');
Route::get('/login/facebook/callback', [App\Http\Controllers\Auth\LoginController::class, 'handleFacebookCallback']);
//Github
Route::get('/login/github', [App\Http\Controllers\Auth\LoginController::class, 'redirectToGithub'])->name('login.github');
Route::get('/login/github/callback', [App\Http\Controllers\Auth\LoginController::class, 'handleGithubCallback']);

第3步:配置服务提供者

你将需要为你的应用程序所使用的OAuth提供商添加凭据。在使用socialite之前,这些凭证应该在你的应用程序的config/services.php 配置文件中。

'google' => [
   'client_id' => env('GOOGLE_CLIENT_ID'),
   'client_secret' => env('GOOGLE_CLIENT_SECRET'),
   'redirect' => 'your_redirect_url',
],
'facebook' => [
   'client_id' => env('FACEBOOK_CLIENT_ID'),
   'client_secret' => env('FACEBOOK_CLIENT_SECRET'),
   'redirect' => 'your_redirect_url',
],
'github' => [
   'client_id' => env('GITHUB_CLIENT_ID'),
   'client_secret' => env('GITHUB_CLIENT_SECRET'),
   'redirect' => 'your_redirect_url',
],

请注意,这个 redirect 值应该是你在服务提供商和你的路由中设置的回调URL。

第四步:在.env 文件中设置CLIENT_ID和CLIENT_SECRET

CLIENT_IDCLIENT_SECRET 不应该出现在任何公共目录中,所以它们应该作为环境变量存储。

  GOOGLE_CLIENT_ID=
  GOOGLE_CLIENT_SECRET=

  FACEBOOK_CLIENT_ID=
  FACEBOOK_CLIENT_SECRET=

  GITHUB_CLIENT_ID=
  GITHUB_CLIENT_SECRET=   

请注意,在不同的服务提供商上创建每个应用程序后,这就是CLIENT_ID和CLIENT_SECRET值应该放置的地方。

第5步:设置LoginController

LoginController 是在 app\Http\Controllers\Auth\LoginController.php 目录下。首先,让我们定义一个_registerOrLoginMethod() ,它将登录返回的用户或注册新用户。这发生在他们从服务提供者的成功认证中被重定向到回调。

protected function _registerOrLoginUser($data){
$user = User::where('email',$data->email)->first();
  if(!$user){
     $user = new User();
     $user->name = $data->name;
     $user->email = $data->email;
     $user->provider_id = $data->id;
     $user->avatar = $data->avatar;
     $user->save();
  }
Auth::login($user);
}

此外,我们现在可以定义我们在路由中为不同的服务提供商设置的不同方法。

//Google Login
public function redirectToGoogle(){
return Socialite::driver('google')->stateless()->redirect();
}

//Google callback  
public function handleGoogleCallback(){

$user = Socialite::driver('google')->stateless()->user();

  $this->_registerorLoginUser($user);
  return redirect()->route('home');
}

//Facebook Login
public function redirectToFacebook(){
return Socialite::driver('facebook')->stateless()->redirect();
}

//facebook callback  
public function handleFacebookCallback(){

$user = Socialite::driver('facebook')->stateless()->user();

  $this->_registerorLoginUser($user);
  return redirect()->route('home');
}

//Github Login
public function redirectToGithub(){
return Socialite::driver('github')->stateless()->redirect();
}

//github callback  
public function handleGithubCallback(){

$user = Socialite::driver('github')->stateless()->user();

  $this->_registerorLoginUser($user);
  return redirect()->route('home');
}

Socialite 立面提供的redirect 方法负责将用户重定向到OAuth提供商,而user 方法将读取传入的请求,并在用户通过认证后从提供商那里获取用户的信息。

第6步:为谷歌、Facebook和GitHub创建一个应用程序

这里有几个片段,简要描述了如何在不同的服务提供商上创建一个应用程序,获得OAuthClient_ID和Client_Secret,并把它们放在一个.env

此后,我们需要清除配置缓存,因为我们更新了环境变量。

使用下面的Artisan 命令:

php artisan config:cache

第7步:为users 表更新migrations

migrations文件在database\migrations\2014_10_12_000000_create_users_table.php 这个目录下。我们需要在up() 方法中添加provider_idavatar 的列,如果用户用他或她的一个社交账户登录的话:

public function up()
{
  Schema::create('users', function (Blueprint $table) {
  $table->id();
  $table->string('name');
  $table->string('email')->unique();
  $table->string('provider_id')->nullable();
  $table->string('avatar')->nullable();
  $table->timestamp('email_verified_at')->nullable();
  $table->string('password')->nullable();
  $table->rememberToken();
  $table->timestamps();
});
}           

现在,用这个Artisan 命令再次运行migrations:

php artisan migrate:fresh

第8步:在导航栏中的用户名字旁边添加用户的头像

当一个用户用他或她的社交账户登录时,我们会获得他们的用户名和头像,以及其他的凭证。

我们需要在用户成功登录后在用户名旁边显示头像。resources\views\layouts\app.blade.php 目录中的nav-item dropdown ,会像这样更新:

<li class="nav-item dropdown">
 <img src="{{Auth::user()->avatar}}" alt="{{Auth::user()->name}}">
   <a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" 
   role="button" data-toggle="dropdown" 
   aria-haspopup="true" aria-expanded="false" v-pre>
              {{ Auth::user()->name }}
  </a>

 <div class="dropdown-menu dropdown-menu-right" 
       aria-labelledby="navbarDropdown">
  <a class="dropdown-item" href="{{ route('logout') }}" 
     onclick="event.preventDefault();  
       document.getElementById('logout-form').submit();">
            {{ __('Logout') }}
      </a>
  <form id="logout-form" action="{{ route('logout') }}" method="POST" class="d-none">
                               @csrf
  </form>
</div>
</li>        

好哇😎!我们已经完成了👍的构建。现在,让我们进行一些测试,看看它是否有效。

测试

在测试之前,这里有几个重要的点需要注意:

  • 用户选择要登录的社交账户,并被重定向到服务提供者。
  • 在服务提供商上成功认证后,用户被登录并被重定向到他或她的仪表板。
  • 如果用户没有注册,由于我们在LoginController 中定义的_registerOrLogin() 方法,他或她将在服务提供商上成功认证后自动注册并登录。

这里有一个简短的片段,描述了如何测试我们目前所建立的一切。

测试Laravel Socialite

注意,我使用了我的个人社交账户来测试,他们都有相同的电子邮件;因此,我不得不从数据库中删除它,然后再使用它来测试另一个社交账户。

结语

非常感谢你对本指南的关注。我希望它能为你提供良好的服务。整个代码是开源的,可以 GitHub上找到,所以你可以查看它来进一步澄清。

谢谢你的阅读 🤝.