Laravel 的 5.3 护照和 api 路线

Laravel's 5.3 passport and api routes

我正在使用 Laravel 框架版本 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、命名个人资料图片,然后从那时起用户被视为已注册。但是用户将 NOT 使用 facebook 登录(这是一个非常重要的方面)。消费者应用程序将获得一个令牌并使用该令牌访问 api 的各种端点(需要令牌才能使用)。

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

到目前为止,在实施 API 相关内容时,Passport 令人头疼,或者我无法弄清楚如何使其正常工作。

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

我正在使用 Postman 访问具有 auth 中间件和以下 JSON application/json

api/v1/users/register 路由
{
    "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.3 passport 的问题在于,与以前由 lucadegasperi 提供的 Laravel 库的 OAuth 2.0 服务器不同,它没有 API 直接创建客户端。所以好像现在只能通过前端来做客户端了。仅供参考,我们只想为我们的移动应用程序使用 laravel 护照,因此在创建和注册用户时,我们只有 EMAIL 和密码,在某些情况下只有 Facebook 用户 ID 用于 facebook 登录。因此,以下方法对我们的案例非常有效,可能会因您的情况而有所不同,但从长远来看可能会帮助您使用 laravel passport。

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

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

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

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

    <?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.'
        ];
    });
    

在上面的代码片段中,您必须注意,要生成 oauth_client 秘密,您必须使用一些您认为适合 table 在您的应用程序中使用的强大加密公式。此外,使用相同的技术在您的移动应用程序上为相应的 client/user.

生成密钥
  1. 现在您可以使用 laravel passport 提供的标准 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 客户端和用户的唯一可能界面的应用程序。

希望对您有所帮助! 干杯。

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

  1. 你几乎从不想把那种服务器进程逻辑放在你的路由目录中。尤其是在创建 API 并打算将其投入生产时。这是一条肮脏的路线,并不完全安全。 UNLESS 它用于在您的路由目录中可以安全处理的事情。例如,在较小的规模上,向工作人员发送有关新 letter/blog/memo 正在发布的通知(短信、电子邮件、推送、松弛)的基本逻辑。

  2. 始终 尝试在尝试 "hackishly" 完成可能具有的任务之前尽可能多地利用和使用框架的功能之前完成过多次

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

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

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

在很多方面,视频教程都做得很好,从头到尾都很透彻。请务必复习一下 OAuth2.0 的不同 Grant_Types,这样您就可以根据应用程序使用 [=94] 的位置更好地了解 you/your 应用程序需要什么类型=]:

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

此外,请务必使用 laravel 的开箱即用功能进行登录,并在创建或登录用户时进行注册。当您在控制台中执行以下操作时,控制器就是为您构建的:

php artisan make:auth

除此之外,如果护照有点神秘,您可以随时提取 laravel/socialite 包裹 (https://github.com/laravel/socialite)。它会让你"Log in with (Social Network Here)"。 提供那是你也打算去的路线。

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

  1. 使用社交名流向 facebook 发送 "login with 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);
            }
        }
    }
    

上面的代码用于在案例你打算使用密码授予类型通过护照验证客户。但是,在开始尝试任何事情之前,我会认真看一下教程视频。它将帮助您了解如何使用 laravel 5.3 护照。