简介
Laravel 通过传统的登录表单已经让用户认证变得很简单,但是 API 认证怎么实现?API 通常使用令牌(token)进行认证并且在请求之间不维护会话(Session)状态。Laravel 官方扩展包 Laravel Passport 让 API 认证变得轻而易举,Passport 基于 Alex Bilbie 维护的 League OAuth2 server,可以在数分钟内为 Laravel 应用提供完整的 OAuth2 服务器实现。OAuth2 概览
正式开始之前我们先简单了解下 OAuth2。 什么是 OAuth 协议 OAuth 是 Open Authorization 的简写,OAuth 协议为用户资源的授权提供了一个安全的、开放而又简易的标准。与以往的授权方式不同之处是 OAuth 的授权不会使第三方触及到用户的帐号信息(如用户名与密码),即第三方无需使用用户的用户名与密码就可以申请获得该用户资源的授权,因此 OAuth 是安全的。 OAuth 本身不存在一个标准的实现,后端开发者自己根据实际的需求和标准的规定实现。其步骤一般如下:客户端要求用户给予授权
用户同意给予授权
根据上一步获得的授权,向认证服务器请求令牌(token)
认证服务器对授权进行认证,确认无误后发放令牌
客户端使用令牌向资源服务器请求资源
资源服务器使用令牌向认证服务器确认令牌的正确性,确认无误后提供资源
Resource Owner(资源拥有者:用户)
Client (第三方接入平台:请求者)
Resource Server (服务器资源:数据中心)
Authorization Server (认证服务器)
注:Passport 需要你对 OAuth2 非常熟悉才能自如使用,上面关于 OAuth2 的概览转自理解 OAuth2.0 认证一文,更多关于 OAuth2 模式的探讨,还可以参考阮一峰博客:理解OAuth 2.0)。
2、安装
首先通过 Composer 包管理器安装 Passport:注:如果安装过程中提示需要更高版本的 Laravel:Passport 服务提供者为框架注册了自己的数据库迁移目录,所以在注册服务提供者之后(Laravel 5.5之后会自动注册服务提供者)需要迁移数据库,Passport 迁移将会为应用生成用于存放客户端和访问令牌的数据表:laravel/passport v5.0.0 requires illuminate/http ~5.6
,可以通过指定版本来安装composer require laravel/passport ~4.0
。
注:如果你不想使用 Passport 的默认迁移,需要在接下来,需要运行AppServiceProvider
的register
方法中调用Passport::ignoreMigrations
方法。你可以使用php artisan vendor:publish --tag=passport-migrations
导出默认迁移。
passport:install
命令,该命令将会创建生成安全访问令牌(token)所需的加密键,此外,该命令还会创建“personal access”和“password grant”客户端用于生成访问令牌: 生成记录存放在数据表 oauth_clients
: 运行完这个命令后,添加 Laravel\Passport\HasApiTokens
trait 到 App\User
模型,该 trait 将会为模型类提供一些辅助函数用于检查认证用户的 token 和 scope: 接下来,你需要在 AuthServiceProvider
的 boot
方法中调用 Passport::routes
方法,该方法将会为颁发访问令牌、撤销访问令牌、客户端以及私人访问令牌注册必要的路由: 最后,在配置文件 config/auth.php
中,需要设置 api
认证 guard 的 driver
选项为 passport
。这将告知应用在认证输入的 API 请求时使用 Passport 的 TokenGuard
:前端快速入门
注:如果要使用 Passport Vue 组件,前端 JavaScript 必须使用 Vue 框架,这些组件同时也使用了 Bootstrap CSS 框架。不过,即使你不使用这些工具,这些组件同样可以为你实现自己的前端组件提供有价值的参考。Passport 附带了 JSON API 以便用户创建客户端和私人访问令牌(access token)。不过,考虑到编写前端代码与这些 API 交互是一件很花费时间的事,Passport 还预置了 Vue 组件作为示例以供使用(或者作为自己实现的参考)。 要发布 Passport Vue 组件,可以使用
vendor:publish
命令: 发布后的组件位于 resources/assets/js/components
目录下,组件发布之后,还需要将它们注册到 resources/assets/js/app.js
文件: 注册完组件后,确保运行 npm run dev
来重新编译前端资源。重新编译前端资源后,就可以将这些组件放到应用的某个模板中以便创建客户端和私人访问令牌:部署 Passport
第一次部署 Passport 到生产服务器时,可能需要运行passport:keys
命令。这个命令生成 Passport 需要的加密 keys 以便生成访问令牌,生成的 keys 将不会存放在源代码控制中:配置
令牌生命周期
默认情况下,Passport 颁发的访问令牌(access token)是长期有效的,如果你想要配置生命周期短一点的令牌,可以使用tokensExpireIn
和 refreshTokensExpireIn
方法,这些方法需要在 AuthServiceProvider
的 boot
方法中调用:颁发访问令牌
通过授权码使用 OAuth2 是大多数开发者熟悉的方式。使用授权码的时候,客户端应用会将用户重定向到你的服务器,服务器可以通过或拒绝颁发访问令牌到客户端的请求。管理客户端
约定:我们参考 OAuth2 认证流程,这里将 Laravel 应用约定为服务方,开发者开发应用作为第三方客户端。首先,开发者构建和 Laravel 应用 API 交互的应用时,需要通过创建一个“客户端”将他们的应用注册到 Laravel 应用。通常,这包括提供应用的名称以及用户授权请求通过后重定向到的 URL。(想想你是怎么使用微博、微信、QQ第三方登录API的,就明白这里的流程了)
passport:client
命令 创建客户端最简单的方式就是使用 Artisan 命令 passport:client
,该命令可用于创建你自己的客户端以方便测试 OAuth2 功能。当你运行 client
命令时,Passport 会提示你输入更多关于客户端的信息,并且为你生成 client ID 和 secret: 新生成记录存放在 oauth_clients
:  JSON API 由于第三方应用开发者不能直接使用 Laravel 服务端提供的 client
命令,为此,Passport 提供了一个 JSON API 用于创建客户端,这省去了你手动编写控制器用于创建、更新以及删除客户端的麻烦。 不过,你需要配对 Passport 的 JSON API 和自己的前端以便为第三方开发者提供一个可以管理他们自己客户端的后台,下面,我们来概览下所有用于管理客户端的 API,为了方便起见,我们将会使用Axios 来演示发送 HTTP 请求到 API:注:如果你不想要自己实现整个客户端管理前端,可以使用前端快速上手教程在数分钟内搭建拥有完整功能的前端。GET
/oauth/clients
这个路由为认证用户返回所有客户端,这在展示用户客户端列表时很有用,可以让用户很容易编辑或删除客户端: POST /oauth/clients
这个路由用于创建新的客户端,要求传入两个数据:客户端的 name
和 redirect
URL, redirect
URL 是用户授权请求通过或拒绝后重定向到的位置。 当客户端被创建后,会附带一个 client ID 和 secret,这两个值会在请求访问令牌时用到。客户端创建路由会返回新的客户端实例: PUT /oauth/clients/{client-id}
这个路由用于更新客户端,要求传入两个参数:客户端的 name
和 redirect
URL。 redirect
URL 是用户授权请求通过或拒绝后重定向到的位置。该路由将会返回更新后的客户端实例: DELETE /oauth/clients/{client-id}
这个路由用于删除客户端:请求令牌
授权重定向 客户端被创建后,开发者就可以使用相应的 client ID 和 secret 从应用请求授权码和访问令牌。首先,客户端应用要生成一个重定向请求到服务端应用的/oauth/authorize
路由:注:我们使用上面创建的 client ID 等于 3 的测试客户端做测试,改写上面的测试代码如下(路由定义在/oauth/authorize
路由已经通过Passport::routes
方法定义了,不需要手动定义这个路由。
routes/api.php
中): 同时在 routes/web.php
注册 auth/callback
路由: 然后在浏览器中访问 http://laravel55.dev/api/redirect
,如果用户尚未在 laravel55
应用中登录,首选会重定向到表单登录页面,登录成功之后就会跳转到第三方授权登录页面 http://laravel55.dev/oauth/authorize?client_id=3&redirect_uri=http%3A%2F%2Flaravel55.dev%2Fauth%2Fcallback&response_type=code&scope=
:  通过请求 接收授权请求的时候,Passport 会自动显示一个视图模板给用户从而允许他们通过或拒绝授权请求(如上图所示),如果用户通过请求,就会被重定向回第三方应用指定的 redirect_uri
(本例中是 http://laravel55.dev/auth/callback
),这个 redirect_uri
必须和客户端创建时指定的 redirect
URL 一致。 如果你想要自定义授权通过界面,可以使用 Artisan 命令 vendor:publish
发布Passport 的视图模板,发布的视图位于 resources/views/vendor/passport
: 将授权码转化为访问令牌 如果用户通过了授权请求,会被重定向回第三方应用。第三方应用接下来会发送一个 POST
请求到服务端应用来请求访问令牌。这个请求应该包含用户通过授权请求时指定的授权码。在这个例子中,我们会使用 Guzzle HTTP 库来生成 POST
请求:/oauth/token
路由会返回一个包含 access_token
、 refresh_token
和 expires_in
属性的 JSON 响应。 expires_in
属性包含访问令牌的过期时间(s): 注:和拿到有效的/oauth/authorize
路由一样,/oauth/token
路由已经通过Passport::routes
方法定义过了,不需要手动定义这个路由。
access_token
就可以通过它去服务端获取其他需要的资源信息了。至此就完成了通过授权码方式实现 API 认证的流程。实际开发中,99% 的 API 认证都是通过这种方式实现的。刷新令牌
如果应用颁发的是短期有效的访问令牌,那么用户需要通过访问令牌颁发时提供的refresh_token
刷新访问令牌,在本例中,我们使用 Guzzle HTTP 库来刷新令牌:/oauth/token
路由会返回一个包含 access_token
、 refresh_token
和 expires_in
属性的 JSON 响应,同样, expires_in
属性包含访问令牌过期时间(s)。密码授权令牌
OAuth2 密码授权允许你的其他第一方客户端,例如移动应用,使用邮箱地址/用户名+密码获取访问令牌。这使得你可以安全地颁发访问令牌给第一方客户端而不必要求你的用户走整个 OAuth2 授权码重定向流程。创建一个密码发放客户端
在应用可以通过密码授权颁发令牌之前,需要创建一个密码授权客户端,你可以通过使用带--password
选项的 passport:client
命令来实现。如果你已经运行了 passport:install
命令,则不必再运行这个命令: 这里我们使用一开始通过 passport:install
命令创建的记录作为测试记录。请求令牌
创建完密码授权客户端后,可以通过发送POST
请求到 /oauth/token
路由(带上用户邮箱地址和密码)获取访问令牌。这个路由已经通过 Passport::routes
方法注册过了,不需要手动定义。如果请求成功,就可以从服务器返回的 JSON 响应中获取 access_token
和 refresh_token
: 请求的时候直接访问 http://laravel55.dev/auth/password
即可。返回数据如下:  和通过授权码返回数据格式一致。注:记住,访问令牌默认长期有效,不过,如果需要的话你也可以配置访问令牌的最长生命周期。
请求所有域
使用密码授权的时候,你可能想要对应用所支持的所有域进行令牌授权,这可以通过请求*
域来实现。如果你请求的是 *
域,则令牌实例上的 can
方法总是返回 true
,这个域只会分配给使用 password
授权的令牌:隐式授权令牌
隐式授权和授权码授权有点相似,不过,无需获取授权码,令牌就会返回给客户端。这种授权通常应用于 JavaScript 或移动应用这些客户端凭证不能被安全存储的地方。要启用该授权,在AuthServiceProvider
中调用 enableImplicitGrant
方法即可: 授权启用后,开发者就可以使用他们的 client ID 从应用中请求访问令牌,第三方应用需要像这样发送重定向请求到应用的 /oauth/authorize
路由:注:/oauth/authorize
路由已经在Passport::routes
方法中定义过了,无需再手动定义这个路由。
客户端凭证授权令牌
客户端凭证授权适用于机器对机器的认证,例如,你可以在调度任务中使用这种授权来通过 API 执行维护任务。要使用这个方法,首先需要在app/Http/Kernel.php
中添加新的中间件到 $routeMiddleware
: 然后将这个中间件应用到路由: 要获取令牌,发送请求到 oauth/token
:私人访问令牌
有时候,你的用户可能想要颁发访问令牌给自己而不走典型的授权码重定向流程。允许用户通过应用的 UI 颁发令牌给自己在用户体验你的 API 或者作为更简单的颁发访问令牌方式时会很有用。注:私人访问令牌总是一直有效的,它们的生命周期在使用tokensExpireIn
或refreshTokensExpireIn
方法时不会修改。
创建一个私人访问客户端
在你的应用可以颁发私人访问令牌之前,需要创建一个私人访问客户端。你可以通过带--personal
选项的 passport:client
命令来实现,如果你已经运行过了 passport:install
命令,则不必再运行此命令:管理私人访问令牌
创建好私人访问客户端之后,就可以使用User
模型实例上的 createToken
方法为给定用户颁发令牌。 createToken
方法接收令牌名称作为第一个参数,以及一个可选的域数组作为第二个参数: JSON API Passport 还提供了一个 JSON API 用于管理私人访问令牌,你可以将其与自己的前端配对以便为用户提供管理私人访问令牌的后台。下面,我们来概览用于管理私人访问令牌的所有 API。为了方便起见,我们使用 Axios 来演示发送 HTTP 请求到 API。注:如果你不想要实现自己的私人访问令牌前端,可以使用前端快速上手教程在数分钟内打造拥有完整功能的前端。GET
/oauth/scopes
这个路由会返回应用所定义的所有域。你可以使用这个路由来列出用户可以分配给私人访问令牌的所有域: GET /oauth/personal-access-tokens
这个路由会返回该认证用户所创建的所有私人访问令牌,这在列出用户的所有令牌以便编辑或删除时很有用: POST /oauth/personal-access-tokens
这个路由会创建一个新的私人访问令牌,该路由要求传入两个参数:令牌的 name
和需要分配到这个令牌的 scopes
: DELETE /oauth/personal-access-tokens/{token-id}
这个路由可以用于删除私人访问令牌:路由保护
通过中间件
Passport 提供了一个认证 guard 用于验证输入请求的访问令牌,当你使用passport
驱动配置好 api
guard 后,只需要在所有路由上指定需要传入有效访问令牌的 auth:api
中间件即可:传递访问令牌
调用被 Passport 保护的路由时,应用 API 的消费者需要在请求的Authorization
头中指定它们的访问令牌作为 Bearer
令牌。例如:令牌作用域
定义作用域
作用域(Scope)允许 API 客户端在请求账户授权的时候请求特定的权限集合。例如,如果你在构建一个电子商务应用,不是所有的 API 消费者都需要下订单的能力,取而代之地,你可以让这些消费者只请求访问订单物流状态的权限,换句话说,作用域允许你的应用用户限制第三方应用自身可以执行的操作。 你可以在AuthServiceProvider
的 boot
方法中使用 Passport::tokensCan
方法定义 API 的作用域。 tokensCan
方法接收作用域名称数组和作用域描述,作用域描述可以是任何你想要在授权通过页面展示给用户的东西:分配作用域到令牌
请求授权码 当使用授权码请求访问令牌时,消费者应该指定他们期望的作用域作为scope
查询字符串参数, scope
参数是通过空格分隔的作用域列表: 颁发私人访问令牌 如果你使用 User
模型的 createToken
方法颁发私人访问令牌,可以传递期望的作用域数组作为该方法的第二个参数:检查作用域
Passport 提供了两个可用于验证输入请求是否经过已发放作用域的令牌认证的中间件。开始使用之前,添加如下中间件到app/Http/Kernel.php
文件的 $routeMiddleware
属性: 检查所有作用域 scopes
中间件会分配给一个用于验证输入请求的访问令牌拥有所有列出作用域的路由: 检查任意作用域 scope
中间件会分配给一个用于验证输入请求的访问令牌拥有至少一个列出作用域的路由: 检查令牌实例上的作用域 当一个访问令牌认证过的请求进入应用后,你仍然可以使用经过认证的 User
实例上的 tokenCan
方法来检查这个令牌是否拥有给定作用域:使用 JavaScript 消费 API
构建 API 时,能够从你的 JavaScript 应用消费你自己的 API 非常有用。这种 API 开发方式允许你自己的应用消费你和其他人分享的同一个 API,这个 API 可以被你的 Web 应用消费,也可以被你的移动应用消费,还可以被第三方应用消费,以及任何你可能发布在多个包管理器上的 SDK 消费。 通常,如果你想要从你的 JavaScript 应用消费自己的 API,需要手动发送访问令牌到应用并在应用的每一个请求中传递它。不过,Passport 提供了一个中间件用于处理这一操作。你所需要做的只是添加这个中间件CreateFreshApiToken
到 web
中间件组: 这个 Passport 中间件将会附加 laravel_token
Cookie 到输出响应,这个 Cookie 包含加密过的JWT,Passport 将使用这个 JWT 来认证来自 JavaScript 应用的 API 请求,现在,你可以发送请求到应用的 API,而不必显示传递访问令牌: 使用这种认证方法时,Axios 会自动发送 X-CSRF-TOKEN
头,此外,Laravel 默认JavaScript 脚手架引入的 Axios 还会发送 X-Requested-With
头。不过你需要确保在 HTML meta 标签中引入了 CSRF 令牌:注:如果你使用的是其它 JavaScript 框架,需要确保每个请求都被配置为发送这两个请求头。
事件
Passport 会在颁发访问令牌和刷新令牌时触发事件,你可以使用这些事件来处理或撤销数据库中的其它访问令牌,你可以在应用的EventServiceProvider
中添加监听器到这些事件:测试
Passport 的actingAs
方法可用于指定当前认证用户及其作用域,传递给 actingAs
方法的第一个参数是用户实例,第二个参数是授权给用户令牌的作用域数组:
30 条评论
你好这个问题你解决了吗?我也遇到了
你好,我也踩到了,请问解决没?
这一块可以参考入门到精通授权码授权这篇文章:https://xueyuanjun.com/laravel-tutorial-5_7#user-auth
./composer.json has been updated Loading composer repositories with package information Updating dependencies (including require-dev) Your requirements could not be resolved to an installable set of packages.
Problem 1
Conclusion: don't install laravel/passport v4.0.3
Conclusion: don't install laravel/passport v4.0.2
Conclusion: don't install laravel/passport v4.0.1
Installation request for laravel/passport ~4.0 -> satisfiable by laravel/passport[v4.0.0, v4.0.1, v4.0.2, v4.0.3].
Conclusion: remove paragonie/random_compat v9.99.99
laravel/passport v4.0.0 requires league/oauth2-server ^6.0 -> satisfiable by league/oauth2-server[6.0.0, 6.0.1, 6.0.2, 6.1.0, 6.1.1].
league/oauth2-server 6.0.0 requires paragonie/random_compat ^2.0 -> satisfiable by paragonie/random_compat[v2.0.0, v2.0.1, v2.0.10, v2.0.11, v2.0.12, v2.0.13, v2.0.14, v2.0.15, v2.0.16, v2.0.17, v2.0.18, v2.0.2, v2.0.3, v2. 0.4, v2.0.5, v2.0.6, v2.0.7, v2.0.8, v2.0.9].
league/oauth2-server 6.0.1 requires paragonie/random_compat ^2.0 -> satisfiable by paragonie/random_compat[v2.0.0, v2.0.1, v2.0.10, v2.0.11, v2.0.12, v2.0.13, v2.0.14, v2.0.15, v2.0.16, v2.0.17, v2.0.18, v2.0.2, v2.0.3, v2. 0.4, v2.0.5, v2.0.6, v2.0.7, v2.0.8, v2.0.9].
league/oauth2-server 6.0.2 requires paragonie/random_compat ^2.0 -> satisfiable by paragonie/random_compat[v2.0.0, v2.0.1, v2.0.10, v2.0.11, v2.0.12, v2.0.13, v2.0.14, v2.0.15, v2.0.16, v2.0.17, v2.0.18, v2.0.2, v2.0.3, v2. 0.4, v2.0.5, v2.0.6, v2.0.7, v2.0.8, v2.0.9].
league/oauth2-server 6.1.0 requires paragonie/random_compat ^2.0 -> satisfiable by paragonie/random_compat[v2.0.0, v2.0.1, v2.0.10, v2.0.11, v2.0.12, v2.0.13, v2.0.14, v2.0.15, v2.0.16, v2.0.17, v2.0.18, v2.0.2, v2.0.3, v2. 0.4, v2.0.5, v2.0.6, v2.0.7, v2.0.8, v2.0.9].
league/oauth2-server 6.1.1 requires paragonie/random_compat ^2.0 -> satisfiable by paragonie/random_compat[v2.0.0, v2.0.1, v2.0.10, v2.0.11, v2.0.12, v2.0.13, v2.0.14, v2.0.15, v2.0.16, v2.0.17, v2.0.18, v2.0.2, v2.0.3, v2. 0.4, v2.0.5, v2.0.6, v2.0.7, v2.0.8, v2.0.9].
Can only install one of: paragonie/random_compat[v9.99.99, v2.0.0].
Can only install one of: paragonie/random_compat[v9.99.99, v2.0.1].
Can only install one of: paragonie/random_compat[v9.99.99, v2.0.10].
Can only install one of: paragonie/random_compat[v9.99.99, v2.0.11].
Can only install one of: paragonie/random_compat[v9.99.99, v2.0.12].
Can only install one of: paragonie/random_compat[v9.99.99, v2.0.13].
Can only install one of: paragonie/random_compat[v9.99.99, v2.0.14].
Can only install one of: paragonie/random_compat[v9.99.99, v2.0.15].
Can only install one of: paragonie/random_compat[v9.99.99, v2.0.16].
Can only install one of: paragonie/random_compat[v9.99.99, v2.0.17].
Can only install one of: paragonie/random_compat[v9.99.99, v2.0.18].
Can only install one of: paragonie/random_compat[v9.99.99, v2.0.2].
Can only install one of: paragonie/random_compat[v9.99.99, v2.0.3].
Can only install one of: paragonie/random_compat[v9.99.99, v2.0.4].
Can only install one of: paragonie/random_compat[v9.99.99, v2.0.5].
Can only install one of: paragonie/random_compat[v9.99.99, v2.0.6].
Can only install one of: paragonie/random_compat[v9.99.99, v2.0.7].
Can only install one of: paragonie/random_compat[v9.99.99, v2.0.8].
Can only install one of: paragonie/random_compat[v9.99.99, v2.0.9].
Installation request for paragonie/random_compat (installed at v9.99.99) -> satisfiable by paragonie/random_compat[v9.99.99].
Installation failed, reverting ./composer.json to its original content.
怎么解决? composer require paragonie/random_compat=~2.0 composer require laravel/passport=~4.0 这种方式可靠不
Passport 的版本不对 安装指定版本试试
/oauth/personal-access-tokens这个路由用了私人令牌访问为什么还一直说未通过认证?