【问题标题】:Laravel's 5.3 passport and api routesLaravel 的 5.3 护照和 api 路由
【发布时间】:2017-01-24 08:34:05
【问题描述】:

我使用的是 Laravel Framework 5.3.9 版,通过 composer 全新下载并没有添加任何内容("laravel/passport": "^1.0" 除外)。

我做了docs 中建议的所有事情。表已创建,路线已启动,一切正常。但是我需要 API 护照。

我的路线如下所示:

+--------+----------+-----------------------------------------+----------------------+----------------------------------------------------------------------------+------------+
| Domain | Method   | URI                                     | Name                 | Action                                                                     | Middleware |
+--------+----------+-----------------------------------------+----------------------+----------------------------------------------------------------------------+------------+
|        | GET|HEAD | /                                       |                      | Closure                                                                    | web        |
|        | GET|HEAD | api/v1/users/register                   | api::users::register | App\Http\Controllers\Api\V1\SocialController@register                      | api,auth   |
|        | POST     | oauth/authorize                         |                      | \Laravel\Passport\Http\Controllers\ApproveAuthorizationController@approve  | web,auth   |
|        | GET|HEAD | oauth/authorize                         |                      | \Laravel\Passport\Http\Controllers\AuthorizationController@authorize       | web,auth   |
|        | DELETE   | oauth/authorize                         |                      | \Laravel\Passport\Http\Controllers\DenyAuthorizationController@deny        | web,auth   |
|        | GET|HEAD | oauth/clients                           |                      | \Laravel\Passport\Http\Controllers\ClientController@forUser                | web,auth   |
|        | POST     | oauth/clients                           |                      | \Laravel\Passport\Http\Controllers\ClientController@store                  | web,auth   |
|        | PUT      | oauth/clients/{client_id}               |                      | \Laravel\Passport\Http\Controllers\ClientController@update                 | web,auth   |
|        | DELETE   | oauth/clients/{client_id}               |                      | \Laravel\Passport\Http\Controllers\ClientController@destroy                | web,auth   |
|        | GET|HEAD | oauth/personal-access-tokens            |                      | \Laravel\Passport\Http\Controllers\PersonalAccessTokenController@forUser   | web,auth   |
|        | POST     | oauth/personal-access-tokens            |                      | \Laravel\Passport\Http\Controllers\PersonalAccessTokenController@store     | web,auth   |
|        | DELETE   | oauth/personal-access-tokens/{token_id} |                      | \Laravel\Passport\Http\Controllers\PersonalAccessTokenController@destroy   | web,auth   |
|        | GET|HEAD | oauth/scopes                            |                      | \Laravel\Passport\Http\Controllers\ScopeController@all                     | web,auth   |
|        | POST     | oauth/token                             |                      | \Laravel\Passport\Http\Controllers\AccessTokenController@issueToken        |            |
|        | POST     | oauth/token/refresh                     |                      | \Laravel\Passport\Http\Controllers\TransientTokenController@refresh        | web,auth   |
|        | GET|HEAD | oauth/tokens                            |                      | \Laravel\Passport\Http\Controllers\AuthorizedAccessTokenController@forUser | web,auth   |
|        | DELETE   | oauth/tokens/{token_id}                 |                      | \Laravel\Passport\Http\Controllers\AuthorizedAccessTokenController@destroy | web,auth   |
+--------+----------+-----------------------------------------+----------------------+----------------------------------------------------------------------------+------------+

所有的web 路由都在那里,没有api 相关的路由,因为 Passport 不提供开箱即用的任何东西。

API 本身旨在供受信任的客户端使用,它是为需要登录的移动应用程序而设计的,但是,所述登录将绕过几个步骤。

一旦用户访问/register 路由,注册过程本身就非常简单:访问用户的 facebook 帐户并获取一些字段 - 电子邮件、facebook id、命名个人资料图片,然后从那时起考虑用户挂号的。但是用户将使用 facebook 登录(这是一个非常重要的方面)。消费者应用程序将获得一个令牌并使用该令牌访问 api 的各种端点(需要使用令牌)。

所以归结为这一点。我需要向访问 API 的消费者应用程序颁发访问令牌。 API 本身只有一个客户端,即移动应用程序本身。使用该应用的用户不被视为 API 的客户端,而是移动应用本身的客户端。

到目前为止,在实现 API 相关的东西时,使用 Passport 是一件令人头疼的事情,或者我不知道如何让它正常工作。

我在oauth_clients 表中创建了一个测试客户端,如下所示:

我正在使用 Postman 访问具有 auth 中间件的 api/v1/users/register 路由和以下 JSON application/json

{
    "grant_type" : "authorization_code",
    "client_id" : 5,
    "client_secet": "y5dvPIOxQJOjYn7w2zzg4c6TRrphsrNFWbG4gAUL"
}

这当然会导致

{"error":"Unauthenticated."}

这很有意义。 出于纯粹的好奇,我将/register 路由更改为:

Route::group([
    'middleware' => [
    ],
], function ()
{
    Route::group([
        'prefix' => 'users',
        'as'     => 'users::',
    ], function ()
    {
//        Route::get('/register', ['as'   => 'register', 'uses' => 'Api\V1\SocialController@register',]);
        Route::post('/register', ['as'   => 'register', 'uses' => '\Laravel\Passport\Http\Controllers\AccessTokenController@issueToken',]);
    });

});

使用与以前相同的json。这导致了{"error":"invalid_client","message":"Client authentication failed"}

我已经找到了处理 vendor/leagueoauth2-server/src/Grant/AbstractGrant` 中的 validateClient 部分的函数。

$client 为空。现在这可能与 Passport 相关,也可能不相关,因为它的文档相当缺乏,并且想通过一个巨大的包裹来追踪错误,这可能主要是由于我没有做正确的事情并没有打动我作为一个好主意,我别无选择。老实说,我什至不知道问题出在哪里。

真的,在这一点上,任何指向正确方向的排序都非常受欢迎。

问题部分是

【问题讨论】:

  • 我没有。我刚决定使用 Laravel 5.2。谢天谢地,这个项目还处于起步阶段,它让我能够做到这一点。
  • 您是否希望在同一应用程序的前端或 API 所属的前端应用程序以外的前端应用程序上利用 API?
  • @Andrew 我也还没有解决。我在我的 Windows 开发服务器上运行,我希望搬到我的 linux 机器上可能会神奇地修复它!
  • @tptcat 我自己我正在尝试将它用于使用 ElectronJS 的外部桌面应用程序以及相同的本地应用程序
  • @tptcat 消费者将成为移动应用程序。

标签: php laravel-5.3


【解决方案1】:

Laravel 5.3 护照的问题在于,与以前由 lucadegasperi 提供的 Laravel 库的 OAuth 2.0 Server 不同,它没有直接创建客户端的 API。所以好像现在客户端只能通过前端制作。仅供参考,我们只想将 laravel 护照用于我们的移动应用程序,因此在创建和注册用户时,我们将只有 EMAIL 和密码,在某些情况下只有 Facebook 用户 ID 用于 facebook 登录。因此,以下方法对我们的情况非常有效,并且可能会因您的情况而有所不同,但从长远来看可能会帮助您使用 laravel 护照。

注意:在执行以下操作之前,假设您已在应用程序中启用密码授予。

所以我们在 laravel 5.3 上为我们的项目解决的方法如下:

  1. 在 oauth_clients 中将 id 字段转换为普通字段,即将其作为主键删除并将数据类型设为 varchar,以便我们可以将电子邮件地址存储为 client_ids,因为它们对于您的系统也是唯一的。在 Facebook 登录的情况下,我们将 Facebook 用户 ID 存储在此列中,这对于我们的每个客户来说都是唯一的。同样对于其他表,例如:oauth_access_tokens、oauth_auth_codes 和 oauth_personal_access_clients 将 client_id 更改为 VARCHAR(255),以便它可以存储电子邮件地址或 Facebook 用户 ID。

  2. 现在转到您的模型并为 oauth_clients 表创建一个模型,以便您可以在创建用户时通过代码以编程方式创建客户端。

    <?php
    namespace App;
    
    
    use Illuminate\Database\Eloquent\Model;
    
    class OauthClient extends Model
    {
        protected $table = 'oauth_clients';
    }
    
  3. 然后在你的 api.php 路由文件中添加以下路由:

    Route::post('/register-user', function (Request $request) {
    
        $name     = $request->input('name');
        $email    = $request->input('email'),
        $password = $request->input('password'),    
    
        // save new user
        $user = \App\User::create([
          'name'     => $name,
          'email'    => $email,
          'password' => bcrypt($password),
        ]);
    
    
        // create oauth client
        $oauth_client = \App\OauthClient::create([
            'user_id'                => $user->id,
            'id'                     => $email,
            'name'                   => $name,
            'secret'                 => base64_encode(hash_hmac('sha256',$password, 'secret', true)),
            'password_client'        => 1,
            'personal_access_client' => 0,
            'redirect'               => '',
            'revoked'                => 0,
        ]);
    
    
        return [
            'message' => 'user successfully created.'
        ];
    });
    

在上面的代码 sn-p 中,您必须注意,要生成 oauth_client 密钥,您必须使用一些强大的加密公式,您可以在应用程序中使用它。此外,使用相同的技术在您的移动应用上为相应的客户/用户生成密钥。

  1. 现在您可以使用 laravel 护照提供的标准 POST API 通过使用“oauth/token”的密码授予请求访问令牌,并使用以下参数:

    grant_type : 'password'
    client_id  : '<email with which the user is registered>'
    client_secret : '<generate the client secret from the mobile app>'
    username : '<email with which the user is registered>'
    password : '<password entered by the user>'
    scope : '<leave empty as default>'
    
  2. 上面会给你一个响应,如果一切都正确的话,类似于:

    {
      "token_type": "Bearer",
      "expires_in": 3155673600,
      "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6IjMwZmM0MDk1NWY5YjUwNDViOTUzNDlmZjc2M2ExNDUxOTAxZjc5YTA5YjE4OWM1MjEzOTJlZmNiMDgwOWQzMzQwM2ExZWI4ZmMyODQ1MTE3In0.eyJhdWQiOiJzaHVqYWhtQGdtYWlsLmNvbSIsImp0aSI6IjMwZmM0MDk1NWY5YjUwNDViOTUzNDlmZjc2M2ExNDUxOTAxZjc5YTA5YjE4OWM1MjEzOTJlZmNiMDgwOWQzMzQwM2ExZWI4ZmMyODQ1MTE3IiwiaWF0IjoxNDc4MTQ1NjMyLCJuYmYiOjE0NzgxNDU2MzIsImV4cCI6NDYzMzgxOTIzMiwic3ViIjoiMSIsInNjb3BlcyI6W119.dj3g9b2AdPCK-im5uab-01SP71S7AR96R0FQTKKoaZV7M5ID1pSXDlmZw96o5Bd_Xsy0nUqFsPNRQsLvYaOuHZsP8v9mOVirBXLIBvPcBc6lDRdNXvRidNqeh4JHhJu9a5VzNlJPm3joBYSco4wYzNHs2BPSxXuuD3o63nKRHhuUHB-HwjVxj2GDwzEYXdZmf2ZXOGRJ99DlWGDvWx8xQgMQtd1E9Xk_Rs6Iu8tycjBpKBaC24AKxMI6T8DpelnFmUbMcz-pRsgCWCF_hxv6FpXav3jr1CLhhT58_udBvXjQAXEbtHeB7W_oaMcaqezHdAeOWDcnqREZHsnXHtKt0JpymcTWBkS2cg7sJzy6P9mOGgQ8B4gb8wt44_kHTeWnokk4yPFRZojkHLVZb8YL6hZxLlzgV1jCHUxXoHNe1VKlHArdlV8LAts9pqARZkyBRfwQ8oiTL-2m16FQ_qGg-9vI0Suv7d6_W126afI3LxqDBi8AyqpQzZX1FWmuJLV0QiNM0nzTyokzz7w1ilJP2PxIeUzMRlVaJyA395zq2HjbFEenCkd7bAmTGrgEkyWM6XEq1P7qIC_Ne_pLNAV6DLXUpg9bUWEHhHPXIDYKHS-c3N9fPDt8UVvGI8n0rPMieTN92NsYZ_6OqLNpcm6TrhMNZ9eg5EC0IPySrrv62jE",
      "refresh_token": "BbwRuDnVfm7tRQk7qSYByFbQKK+shYPDinYA9+q5c/ovIE1xETyWitvq6PU8AHnI5FWb06Nl2BVoBwCHCUmFaeRXQQgYY/i5vIDEQ/TJYFLVPRHDc7CKILF0kMakWKDk7wJdl5J6k5mN38th4pAAZOubiRoZ+2npLC7OSZd5Mq8LCBayzqtyy/QA5MY9ywCgb1PErzrGQhzB3mNhKj7U51ZnYT3nS5nCH7iJkCjaKvd/Hwsx2M6pXnpY45xlDVeTOjZxxaOF/e0+VT2FP2+TZMDRfrSMLBEkpbyX0M/VxunriRJPXTUvl3PW0sVOEa3J7+fbce0XWAKz7PNs3+hcdzD2Av2VHYF7/bJwcDCO77ky0G4JlHjqC0HnnGP2UWI5qR+tCSBga7+M1P3ESjcTCV6G6H+7f8SOSv9FECcJ8J5WUrU+EHrZ95bDtPc9scE4P3OEQaYchlC9GHk2ZoGo5oMJI6YACuRfbGQJNBjdjxvLIrAMrB6DNGDMbH6UZodkpZgQjGVuoCWgFEfLqegHbp34CjwL5ZFJGohV+E87KxedXE6aEseywyjmGLGZwAekjsjNwuxqD2QMb05sg9VkiUPMsvn45K9iCLS5clEKOTwkd+JuWw2IU80pA24aXN64RvOJX5VKMN6CPluJVLdjHeFL55SB7nlDjp15WhoMU1A="
    }
    

在 laravel 支持应用程序的外部 API 之前,它只是一个临时解决方案,只有移动设备作为创建 oAuth 客户端和用户的唯一可能接口。

希望对你有帮助! 干杯。

【讨论】:

  • 呃,我讨厌它不提供这个,而不是通过它来破解你的方式。无论哪种方式,很好的答案。
  • @Andrew 很高兴它对您有所帮助。让我们等到 laravel 将其作为路由服务提供者的一部分。
  • 这是一个很好的答案。我正在为移动应用程序编写身份验证过程,但我不确定逻辑是否正确,在谷歌搜索时我找到了这个答案来确认它。不过,我不会根据用户的电子邮件和密码生成新客户端。如果移动应用程序在第一次注册后“忘记”了客户端 ID 和密码,用户必须再次输入用户名和密码,生成一个全新的客户端 ID 和密码对,如果用户名和密码正确,该对将返回给应用程序。然后,通过 user、pass、clientid、secret,应用可以请求访问令牌并存储它。
  • @Jack 非常感谢。很高兴它帮助了你。你介意给它投票:-)
  • 非常感谢,这和我的情况类似。
【解决方案2】:

因为标记的答案被认为是正确的,所以我觉得有必要注意一些我认为许多人会同意的关键点:

  1. 您几乎绝不希望将这种服务器进程逻辑放在您的路由目录中。尤其是在创建 API 以将其投入生产时。这是一条肮脏的路线,并不完全安全。 除非用于在您的路线目录中安全处理的内容。例如,在较小的范围内,向员工发送通知(SMS、电子邮件、推送、slack)的基本逻辑是关于正在发布的新信件/博客/备忘录。

  2. 总是在尝试“粗暴地”完成之前可能已经完成多次的任务之前,尝试尽可能多地利用和利用框架的功能。

  3. 确保您正在对已经完成的事情进行适当的研究。这样一来,您就可以更轻松地简单地参考一个视频或教程,这些视频或教程展示了如何正确地做某人正在尝试做的事情。

话虽如此,观看以下视频是一个很好的起点,该视频完美地描述了如何正确设置您要设置的内容的基础知识:

https://laracasts.com/series/whats-new-in-laravel-5-3/episodes/13

在许多方面,视频教程都做得很好,从头到尾都很彻底。请务必复习 OAuth2.0 的不同 Grant_Types,以便您根据应用程序使用 api 的位置更好地了解您/您的应用程序需要什么特定类型:

https://www.digitalocean.com/community/tutorials/an-introduction-to-oauth-2

另外,在创建或登录用户时,一定要使用 laravel 开箱即用的登录和注册功能。当您在控制台中执行以下操作时,将为您构建控制器:

php artisan make:auth

除此之外,如果护照有点神秘,您可以随时使用 laravel/socialite 包 (https://github.com/laravel/socialite)。它将允许您“使用(此处的社交网络)登录”。 如果那是您也打算走的路线。

结束说明:我在您的问题中看到的最突出的部分是一个人如何注册但不会使用 facebook 登录。相反,将有一个访问令牌来访问各种 API 端点。因此,如果我得到您所说的正确,您的目标是在返回数据时使用来自 facebook 的用户数据,该用户被视为已登录,并将获得访问令牌。所以:

  1. 使用社交名流向 facebook 发送“使用 facebook 登录”请求。这将获取用户的数据并利用一些 facebook 的身份验证过程。

  2. 当请求在正文中返回带有用户数据的请求时,请通过检查以确保存在数据(简单的 if 语句应该没问题)。由于 facebook 已经对该用户和发送的凭据进行了身份验证,因此您应该一切顺利。

  3. 您可以在登录控制器中启动内部代理(这是更清洁、更安全的方法),也可以发出 JWT(在上面这个答案中发布的视频的最后 5 分钟中介绍了这一点)。

    以下是一些帮助您入门的示例代码。

    App\Http\Controllers\Auth\LoginController.php

    class LoginController extends Controller
    {
    
        // ...
    
        protected function authenticateClient(Request $request) {
    
            $credentials = $this->credentials($request);
    
            $data = $request->all();
    
            $user = User::where('email', $credentials['email'])->first();
    
            $request->request->add([
                'grant_type'    => $data['grant_type'],
                'client_id'     => $data['client_id'],
                'client_secret' => $data['client_secret'],
                'username'      => $credentials['email'],
                'password'      => $credentials['password'],
                'scope'         => null,
            ]);
    
            $proxy = Request::create(
                'oauth/token',
                'POST'
            );
    
            return Route::dispatch($proxy);
        }
    
        protected function authenticated(Request $request, $user) {
    
            return $this->authenticateClient($request);
        }
    
        protected function sendLoginResponse(Request $request)
        {
            $request->session()->regenerate();
    
            $this->clearLoginAttempts($request);
    
            return $this->authenticated($request, $this->guard()->user());
    
        }
    
        public function login(Request $request)
        {
    
            if ($this->guard('api')->attempt($credentials, $request->has('remember'))) {
    
                return $this->sendLoginResponse($request);
            }
        }
    }
    

上面的代码用于IN CASE,您打算使用Password Grant 类型通过护照对客户进行身份验证。但是,在动手做任何事情之前,我都会认真看一下教程视频。它将帮助您了解如何将 laravel 5.3 与护照一起使用。

【讨论】:

  • 我完全同意这一切。在从文档中没有清楚地了解哪些部分属于消费者或服务器之后,我跟随了视频。现在我有了这个基础,我可以根据自己的需要定制它。将此代码放在控制器中还有一个非常重要且经常被忽视的额外好处:artisan route:cache 加速了典型的 Laravel 应用程序显着,它绝对不适用于闭包。所有路由(在所有包中)都需要映射到控制器方法,否则您将失去优化。
猜你喜欢
  • 2017-11-06
  • 2017-02-11
  • 2017-02-28
  • 1970-01-01
  • 2019-01-05
  • 2017-12-01
  • 2019-09-13
  • 2019-06-23
  • 2017-05-27
相关资源
最近更新 更多