Laravel的5.3护照和api路线

时间:2016-09-16 07:26:55

标签: php laravel-5.3

我正在使用Laravel Framework版本5.3.9,通过作曲家添加了新的下载("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帐户,抓住几个字段 - 电子邮件,脸谱ID,命名个人资料图片,从那时起用户被视为已注册。但是用户将登录Facebook(这是一个非常重要的方面)。将向消费者应用程序发出一个令牌,并使用该令牌访问api的各个端点(需要使用令牌)。

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

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

我在oauth_clients表中创建了一个看起来像这样的测试客户端:

enter image description here

我正在使用Postman访问具有api/v1/users/register中间件的auth路由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有关,也可能没有关系,因为它上面的文档相当缺乏,并且想通过一个包的怪物来挖掘错误可能主要是因为我没有做对作为一个好主意,我没有选择。说实话,我甚至不知道问题是什么。

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

问题中的部分是

2 个答案:

答案 0 :(得分:21)

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

注意:在遵循以下内容之前,假设您已在应用程序中启用了密码授予。

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

    oauth_clients中的
  1. 将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.'
        ];
    });
    
  4. 在上面的代码片段中,您必须注意,要生成oauth_client密钥,您必须使用一些强大的加密公式,您觉得将其与您的应用程序一起使用是很舒服的。此外,使用相同的技术在您的移动应用程序上为相应的客户端/用户生成密钥。

    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="
      }
      
    3. 它只是一个临时解决方案,直到laravel为只有移动设备作为创建oAuth客户端和用户的唯一可能接口的应用程序支持外部API。

      希望它对你有所帮助! 欢呼声。

答案 1 :(得分:10)

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

  1. 您几乎从不希望在路由目录中放置此类服务器进程逻辑。特别是在创建一个旨在将其投入生产的API时。这是一条肮脏的路线,并不完全安全。 除非它适用于在您的路线目录中可以安全处理的事物。比如规模较小的基础逻辑,用于向工作人员发送关于作为示例发布的新信件/博客/备忘录的通知(SMS,电子邮件,推送,松弛)。

  2. 始终尝试利用并尽可能多地使用框架的功能,然后尝试“hackishly”完成之前可能多次完成的任务。

  3. 确保您正在对已经完成的事情进行适当的研究。通过这种方式,它可以更轻松地简单地引用视频或教程,以显示如何正确地执行某人正在尝试做的事情。

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

    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的用户数据,用户被认为已登录并将获得访问令牌。 SO:

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

    2. 当一个请求返回时,正文中的用户数据通过检查运行它以确保有数据(简单的if语句应该没问题)。由于Facebook已经对该用户进行了身份验证,并且您应该的发送凭据不错。

    3. 您可以在登录控制器中启动内部代理(这是更干净,更安全的方式),或者您可以发布JWT(在发布的视频的最后5分钟内介绍)上面这个答案)。

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

      应用\ HTTP \控制器\验证\ 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);
              }
          }
      }
      
    4. 上面的代码用于 IN CASE ,您的目标是使用密码授予类型通过护照验证客户端。但是,在开枪之前我会认真看一下教程视频。如果使用laravel 5.3和护照,它将帮助你解决很多问题。