欢迎各位兄弟 发布技术文章

这里的技术是共享的

You are here

[Laravel 5.3 新功能] 11. Laravel Passport 介绍

说明#

此文章是 [Laravel 5.3 新功能] 系列的第十一篇文章,Laravel Passport 介绍。

[Laravel 5.3 新功能] 系列完整文章列表请见:https://laravel-china.org/topics/2638

注意事项#

在编写此文章时,Passport 还未正式发布,因此可能会和发布版本有所出入。我会在其正式发布后第一时间更新此文章。

什么是 Laravel Passport#

API 的身份验证是我们开发中最棘手的部分。OAuth 是你可以考虑的比较严格的验证方式,但是它比较难集成。我们曾经有讨论过使用多个路由、migration、复杂的配置等等来简化身份验证的集成难度,但实际上集成此服务还是过于复杂。

不过,在 5.3 以后出现了 Laravel Passport,它是一个为 Laravel 提供原生 OAuth 2 服务的组件。类似于 Cashier 和 Scout,你可通过 Composer 将其安装到你的 Laravel 应用程序中,而且它集成非常简单。

Laravel 5.2 中身份认证的弊端#

在 Laravel 5.2 里,我们利用多目录来为身份认证提供多个 auth 认证服务,这意味着你可以在不同的环境中使用不同的身份认证方式(如 web 和 api,或者生产环境和开发环境)。

在 5.2 中,默认的 token 的身份验证机制已经足够使用了,但是它对于需要登录操作的业务来说不够安全,所以我们需要 passport 来提供更安全的 OAuth2 认证机制。

安装 Passport#

1)、运行下面的命令,通过 Composer 安装 Passport

composer require laravel/passport

2)、打开你的 config/app.php 文件,将 Laravel\Passport\PassportServiceProvider 添加到 $providers 属性中。

3)、运行 migrations

php artisan migrate

4)、打开你的 User 类,并引入 Laravel\Passport\HasApiTokens traits。

5)、在你的 AuthServiceProvider 文件中引入 use Laravel\Passport\Passport,然后在 boot() 方法中添加 OAuth2 路由方法 Passport::routes()

// AuthServiceProvider
public function boot()
{
    Passport::routes();
}

6)、 加入 Passport::tokensCan() 方法

// AuthServiceProvider
public function boot()
{
    Passport::route();
    Passport::tokensCan([
        'conference' => 'Access your conference information'
    ]);
}

7)、在 config/auth.php 文件中,修改 guards.api.driver 配置,将 driver 设置为 passport

// config/auth.php
return [
    ...
    'guards' => [
        ...
        'api' => [
            'driver' => 'passport', // was previously 'token'
            'provider' => 'users'
        ]
    ]
];

这样就全部配置成功了,非常简单吧。接下来将讲解如何管理你的客户端和 token。

Passport 如何管理 API#

Passport 通过 JSON 格式的 API 提供服务给客户端,这些 API 都提供了管理(新增、删除等)客户端和个人 token 的方法。

Passport 默认使用 Vue 组件来展示你的 API,你可以使这个组件来展示和管理你的 API。当然,你也可以编写自定义的工具来集成 API。

Passport 默认使用 Vue 框架#

Passport 使用了三个 Vue 组件:

<!-- let people make clients -->
<passport-clients></passport-clients>

<!-- list of clients people have authorized to access our account -->
<passport-authorized-clients></passport-authorized-clients>

<!-- make it simple to generate a token right in the UI to play with -->
<passport-personal-access-tokens></passport-personal-access-tokens>

我们来分析一下它们都做了什么。

在 Laracon 上,Taylor 做了一个例子。他先在 http://passport.dev/ 应用程序上安装了 Passport,使其能提供 OAuth 服务。然后他再创建一个 http://consumer.dev/ 应用程序作为客户端,下文中我们称其为 consumer

下面是他展示的管理界面(使用了上文提到的 vue 的三个组件)
file

下面是创建 client 的界面
file

当你创建完 client 后,你会获得一个 client ID,现在将此 ID 放到上文提到的 consumer 应用程序中,写入到其配置文件里。

下面是 Taylor 展示的 consumer 应用程序路由:

// routes/web.php

use Illuminate\Http\Request;

// First route that user visits on consumer app
Route::get('/', function () {
    // Build the query parameter string to pass auth information to our request
    $query = http_build_query([
        'client_id' => 1,
        'redirect_uri' => 'http://consumer.dev/callback',
        'response_type' => 'code',
        'scope' => 'conference'
    ]);

    // Redirect the user to the OAuth authorization page
    return redirect('http://passport.dev/oauth/authorize?' . $query);
});

// Route that user is forwarded back to after approving on server
Route::get('callback', function (Request $request) {
    $http = new GuzzleHttp\Client;

    $response = $http->post('http://passport.dev/oauth/token', [
        'form_params' => [
            'grant_type' => 'authorization_code',
            'client_id' => 1, // from admin panel above
            'client_secret' => 'abc', // from admin panel above
            'redirect_uri' => 'http://consumer.dev/callback`,
            'code' => $request->code // Get code from the 
        ]
    ]);

    return json_decode((string) $response->getBody(), true)['access_token'];
});

当你使用配置好的 client ID 等数据访问 http://consumer.dev/ 时,它会自动重定向到 http://passport.dev/ 里。
file

当你认证成功后,Passport 将重定向回你的 callback URL,在上文的例子中,它将重定向回 http://consumer.dev/callback,现在你将获得一个 token,通过这个 token 你能获取到用户的相关权限。

测试你的 token#

在轻松的做完这一切后,Taylor 在他的 Passport 应用程序中创建了一个路由文件 routes/api.php,我们看一下如何使用其便捷的测试你的 API。

首先,在 Passport 应用程序路由中添加以下代码:

// routes/api.php
Route::get('/user', function (Request $request) {
    return $request->user();
});

由于在 routes/api.php 中的所有路由会自动加载 API 中间件,所以它会自动条用 auth:api 来验证用户。

接下来,打开你常用的 REST 客户端(如 Postman,Paw 又或者是你自己手写 PHP 代码请求接口)请求接口 http://passport.dev/api/user,当然你首先需要确认你的请求中 header 的 Content-Type 和接收信息中 header 的 Accept 都为 application/json

如果验证不通过,你会收到 401 回应:

{
  "error": "Unauthenticated."
}

现在,你想起来 access token 没?复制这个 token,然后将其加入到你请求 header 的 Authorization 中,设置它的值为 Bearer: TOKENHERE,TOKENHERE 就是你刚才获取复制的 token 了。现在,你应该能看到正常的返回值了:

{
  "id": 1,
  "name": "Matt Stauffer",
  "email": "matt@mattstauffer.co",
  "created_at": "2016-07-30 10:45:00",
  "updated_at": "2016-07-30 10:45:00"
}

就这样,你就快速的配置好你的 OAuth 2 API 了。

Passport 还有其他的功能,我们一起来看一下。

管理客户端 token 界面#

file

请记住,这只是一个示范界面,你不一定使用 vue 来构建这个界面。

Passport 提供了非常有用的工具来创建测试 token,你不需要再创建一个完整的 consumer 应用程序来测试你的 API,你可以创建 personal tokens 来进行测试:

php artisan make passport:client --personal

现在你可以去 Personal Access Tokens 界面点击 “Create New Token” 按钮,它将生成新的 token。你可以删除这个 token,就像你可以撤销你的 client tokens 一样。

Scope 中间件#

使用 scope 可以定义你的应用程序中的每个请求可以使用何种中间件。每个 scope 都有名称和描述,这样方便在程序中调用。

我们现在来看一下如何简单高效的定义 scope 中间件。

现在你的应用程序中有两个中间件,你可以给他们添加别名方便你快速调用,在这里我们分别起名为 anyScope 和 allScopes

现在修改一下 app/Http/Kernel.php 文件:

// App\Http\Kernel
...
protected $routeMiddleware = [
    ...
    // you can name these whatever you want
    'anyScope' => \Laravel\Passport\Http\Middleware\CheckForAnyScope::class,
    'allScopes' => \Laravel\Passport\Http\Middleware\CheckScopes::class,
];

现在,在每个路由后面你都可以调用这两个中间件进行访问权限控制,如果你使用 anyScope,则当用户拥有任一指定的 scope 即可访问该路由。如果你使用 allScopes,则用户必须拥有所有指定的 scope 才可以访问该路由。

在下面的例子中,程序将限制用户的访问权限,除非他们有 conference scope。

Route::get('/whatever', function () {
    // do stuff
})->middleware('anyScope:conference');

// Any of the given scopes
Route::get('/whatever', function () {
    // do stuff
})->middleware('anyScope:conference,otherScope');

// All of the given scopes
Route::get('/whatever', function () {
    // do stuff
})->middleware('allScopes:conference,otherScope');

结语#

我编写过很多的 OAuth 服务,这过程非常痛苦。Passport 是我非常喜欢的新功能,不仅仅是因为它简化了我之前非常痛恨的集成,它还提供了很多我之前没想到的新功能,我现在很迫不及待的想使用它。

全文完

链接#

 本帖已被设为精华帖!
 
本帖由 monkey 于 1年前 加精
回复数量: 13
  • 杨进春
     ⋅ 1年前

    我现在也迫不及待的想使用它。

  • Summer MOD A Life-long learner.
     ⋅ 1年前

    我现在也迫不及待的想使用它。

  • lijy91
    lijy91
     ⋅ 1年前

    刚把项目迁移到 5.3 了,原来是用 jwt-auth 做JSON Web Token 认证的,对Oauth2不是太了解。
    想请教一下老司机,我的用法是否正确

    验证用户名密码后为用户生成 access_token,客户端通过该Token来验证当前用户登录状态。

    public function login(Request $request)
    {
        $rules = [
            'email'    => 'required|email|exists:users',
            'password' => 'required|between:6,32',
            'username' => 'min:5|max:32|alpha_dash|exists:users',
        ];
        $this->validate($request, $rules);
    
        $credentials = $request->only('email', 'password');
    
        if (!Auth::attempt($credentials)) {
            return $this->failure(trans('auth.failed'), 401);
        }
    
        $user = Auth::user();
        // 设置访问令牌对象
        $user->jwt_token = [
            'access_token' => $user->createToken('Token Name')->accessToken,
            'expires_in'   => Carbon::now()->subMinutes(config('jwt.ttl'))->timestamp
        ];
        return $this->success($user);
    }

    代码:https://github.com/lijy91/daza-backend/blob/master/app/Http/Controllers/AccountController.php

    这是项目地址:https://github.com/lijy91/daza-backend
    这是文档地址:http://mock-web.daza.io

  • lijy91
    lijy91
     ⋅ 1年前
  • tonyboy
     ⋅ 1年前

    太酷炫

  • jiemoon
     ⋅ 1年前

    @monkey 大神,您要全屏了 :thumbsup:

  • Summer MOD A Life-long learner.
     ⋅ 1年前

    图片好模糊

  • mcxzyang
    mcxzyang a php developer
     ⋅ 1年前

    开始刷屏了

  • Deadalus
    Deadalus
     ⋅ 11个月前

    测试token

    请求头那里 "Authorization: Bearer TOKENHERE"
    bearer和token中间没有冒号
    原文也是错的。。

  • JokerLinly 安正超粉丝协会会长大人 :see_no_evil:
     ⋅ 5个月前

    我亲爱的猴子先生,你太赞了,我要的东西你都有。

  • houseme
     ⋅ 2个月前

    mark

  • storefee
    storefee
     ⋅ 1个月前

    Bearer: TOKENHERE 这个地方千万不要加上(冒号)“:”,而是(空格),正确姿势:Bearer TOKENHERE,不然始终都是Unauthenticated。一个冒号引发的2个小时的调试和各种文章的查找。

  • storefee
    storefee
     ⋅ 1个月前

    碰到其他类似情况可以看看这段:
    Looks like token guard is just a simple token solution which is like using a password. TokenGuard is looking for the token in 3 places:

    in the URL for parameter ?api_token=XXX
    in the header for "Authorization: Bearer XXX". Which is used in JWT, Oauth, etc.
    in the header for "Authorization: Basic XXX". Which is Basic HTTP auth where XXX is base64 encoded username:password. The password is used as the token.

    出处:https://gistlog.co/JacobBennett/090369fbab0b31130b51

 

普通分类: