[ Laravel 5.3 文档 ] 安全 ―― API认证(Passport)保障安全性。

时间:2022-08-06 00:40:14

1、简介

Laravel通过传统的登录表单已经让用户认证变得很简单,但是API怎么办?API通常使用token进行认证并且在请求之间不维护session状态。Laravel使用LaravelPassport让API认证变得轻而易举,Passport基于Alex Bilbie维护的 League OAuth2 server ,可以在数分钟内为Laravel应用提供完整的OAuth2服务器实现。

注:本文档假设你已经很熟悉OAuth2,如果你对OAuth2一无所知,那么在开始学习文文档之前,先要去熟悉OAuth2的一些术语和特性(参考阮一峰博客: 理解OAuth 2.0 )。

2、安装

使用Composer包管理器安装Passport:

composer require laravel/passport

接下来,在配置文件 config/app.php 的 providers 数组中注册Passport服务提供者:

Laravel\Passport\PassportServiceProvider::class,

Passport服务提供着为框架注册了自己的数据库迁移目录,所以在注册之后需要迁移数据库,Passport迁移将会为应用生成用于存放客户端和访问令牌的数据表:

php artisan migrate

接下来,需要运行 passport:install 命令,该命令将会创建生成安全访问令牌(token)所需的加密键,此外,该命令还会创建“personal access”和“password grant”客户端用于生成访问令牌:

php artisan passport:install

运行完这个命令后,添加 Laravel\Passport\HasApiTokens trait到 App\User 模型,该trait将会为模型类提供一些辅助函数用于检查认证用户的token和scope:

<?php
namespace App;
use Laravel\Passport\HasApiTokens;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
use HasApiTokens, Notifiable;
}

接下来,你需要在 AuthServiceProvider 的 boot 方法中调用 Passport::routes 方法,该方法将会注册发布/撤销访问令牌、客户端以及私人访问令牌所必需的路由:

<?php
namespace App\Providers;
use Laravel\Passport\Passport;
use Illuminate\Support\Facades\Gate;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
class AuthServiceProvider extends ServiceProvider
{
/**
* The policy mappings for the application.
*
* @var array
*/
protected $policies = [
'App\Model' => 'App\Policies\ModelPolicy',
];
/**
* Register any authentication / authorization services.
*
* @return void
*/
public function boot()
{
$this->registerPolicies();
Passport::routes();
}
}

最后,在配置文件 config/auth.php 中,需要设置 api 认证guard的 driver 选项为 passport 。这将告知应用在认证输入的API请求时使用Passport的 TokenGuard :

'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'passport',
'provider' => 'users',
],
], 前端快速上手

注:为了使用Passport的Vue组件,前端javascript必须使用Vue框架,这些组件同时也使用了Bootstrap CSS框架。不过,即使你不使用这些工具,这些组件同样可以为你实现自己的前端组件提供有价值的参考。

Passport附带了JSON API以便用户创建客户端和私人访问令牌(access token)。不过,考虑到编写前端代码与这些API交互是一件很花费时间的事,Passport还预置了Vue组件作为示例以供使用(或者作为自己实现的参考)。

要发布Passport Vue组件,可以使用 vendor:publish 命令:

php artisan vendor:publish --tag=passport-components

发布后的组件位于 resources/assets/js/components 目录下,组件发布之后,还需要将它们注册到 resources/assets/js/app.js 文件:

Vue.component(
'passport-clients',
require('./components/passport/Clients.vue')
);
Vue.component(
'passport-authorized-clients',
require('./components/passport/AuthorizedClients.vue')
);
Vue.component(
'passport-personal-access-tokens',
require('./components/passport/PersonalAccessTokens.vue')
);

注册完成后,就可以将它们放到应用的某个模板中以便创建客户端和私人访问令牌:

<passport-clients></passport-clients>
<passport-authorized-clients></passport-authorized-clients>
<passport-personal-access-tokens></passport-personal-access-tokens> 3、配置 令牌生命周期

默认情况下,Passport颁发的访问令牌(access token)是长期有效的,如果你想要配置更短的令牌生命周期,可以使用 tokensExpireIn 和 refreshTokensExpireIn 方法,这些方法需要在 AuthServiceProvider 的 boot 方法中调用:

/**
* Register any authentication / authorization services.
*
* @return void
*/
public function boot()
{
$this->registerPolicies();
Passport::routes();
Passport::tokensExpireIn(Carbon::now()->addDays(15));
Passport::refreshTokensExpireIn(Carbon::now()->addDays(30));
精简撤销令牌

默认情况下,Passport不会从数据库删除撤销的访问令牌,随着时间的推移,在数据库中会累积大量的撤销令牌,如果你希望Passport可以自动删除撤销的令牌,可以在 AuthServiceProvider 的 boot 方法中调用 pruneRevokedTokens 方法:

use Laravel\Passport\Passport;
Passport::pruneRevokedTokens();

该方法不会立即删除所有的撤销令牌,而是当用户请求一个新的访问令牌或者刷新已存在令牌的时候才会删除撤销的令牌。

4、颁发访问令牌

通过授权码使用OAuth2是大多数开发者属性的方式。使用授权码的时候,客户端应用会将用户重定向到你的服务器,服务器将会通过或决绝颁发访问令牌到客户端的请求。

管理客户端

首先,开发构建和你的应用API交互的应用时,需要通过创建一个“客户端”以便将他们的应用注册到你的应用。通常,这一过程包括提供应用的名称以及用户授权请求通过后重定向到的URL。

passport:client命令

创建客户端最简单的方式就是使用Artisan命令 passport:client ,该命令可用于创建你自己的客户端以方便测试OAuth2功能。当你运行 client 命令时,Passport会提示你关于客户端的更多信息,并且为你提供client ID和secret:

php artisan passport:client JSON API

由于用户不能使用client命令,Passport提供了一个JSON API用于创建客户端,这省去了你手动编写控制器用于创建、更新以及删除客户端的麻烦。

不过,你需要配对Passport的JSON API和自己的前端以便为用户提供一个可以管理他们自己客户端的后台,下面,我们来回顾下所有用于管理客户端的API,为了方便,我们将会使用Vue来演示发送HTTP请求到API:

注:如果你不想要自己实现整个客户端管理前端,可以使用前端快速上手教程在数分钟内拥有完整功能的前端。

GET /oauth/clients

这个路由为认证用户返回所有客户端,这在展示用户客户端列表时很有用,可以让用户很容易编辑或删除客户端:

this.$http.get('/oauth/clients')
.then(response => {
console.log(response.data);
});

POST /oauth/clients

这个路由用于创建新的客户端,要求传入两个数据:客户端的 name 和 redirect URL, redirect URL是用户授权请求通过或拒绝后重定向到的位置。

当客户端被创建后,会附带一个client ID和secret,这两个值会在请求访问令牌时用到。客户端创建路由会返回新的客户端实例:

const data = {
name: 'Client Name',
redirect: 'http://example.com/callback'
};
this.$http.post('/oauth/clients', data)
.then(response => {
console.log(response.data);
})
.catch (response => {
// List errors on response...
});

PUT /oauth/clients/{client-id}

这个路由用于更新客户端,要求传入两个参数:客户端的 name 和 redirect URL。 redirect URL是用户授权请求通过或拒绝后重定向到的位置。该路由将会返回更新后的客户端实例:

const data = {
name: 'New Client Name',
redirect: 'http://example.com/callback'
};
this.$http.put('/oauth/clients/' + clientId, data)
.then(response => {
console.log(response.data);
})
.catch (response => {
// List errors on response...
});

DELETE /oauth/clients/{client-id}

这个路由用于删除客户端:

this.$http.delete('/oauth/clients/' + clientId)
.then(response => {
//
}); 请求令牌 授权重定向

客户端被创建后,开发者就可以使用相应的client ID和secret从应用请求授权码和访问令牌。首先,消费者应用要生成一个重定向请求到应用的 /oauth/authorize 路由:

Route::get('/redirect', function () {
$query = http_build_query([
'client_id' => 'client-id',
'redirect_uri' => 'http://example.com/callback',
'response_type' => 'code',
'scope' => '',
]);
return redirect('http://your-app.com/oauth/authorize?'.$query);
});

注:记住, /oauth/authorize 路由已经通过 Passport::routes 方法定义了,不需要手动定义这个路由。

通过请求

接收授权请求的时候,Passport会自动显示一个视图模板给用户从而允许他们通过或拒绝授权请求,如果用户通过请求,就会被重定向回消费者应用指定的 redirect_uri ,这个 redirect_uri 必须和客户端创建时指定的 redirect URL一致。

如果你想要自定义授权通过界面,可以使用Artisan命令 vendor:publish 发布Passport的视图模板,发布后的视图位于 resources/views/vendor/passport :

php artisan vendor:publish --tag=passport-views 将授权码转化为访问令牌

如果用户通过了授权请求,会被重定向回消费者应用。消费者接下来会发送一个 POST 请求到应用来请求访问令牌。这个请求应该包含用户通过授权请求时指定的授权码。在这个例子中,我们会使用Guzzle HTTP库来生成POST请求:

Route::get('/callback', function (Request $request) {
$http = new GuzzleHttp\Client;
$response = $http->post('http://your-app.com/oauth/token', [
'form_params' => ['grant_type' => 'authorization_code','client_id' => 'client-id','client_secret' => 'client-secret','redirect_uri' => 'http://example.com/callback','code' => $request->code,
],
]);
return json_decode((string) $response->getBody(), true);
});

/oauth/token 路由会返回一个包含 access_token 、 refresh_token 和 expires_in 属性的JSON响应。 expires_in 属性包含访问令牌的过期时间(s)。

注:和 /oauth/authorize 路由一样, /oauth/token 路由已经通过 Passport::routes 方法定义过了,不需要手动定义这个路由。

刷新令牌

如果应用颁发的是短期有效的访问令牌,那么用户需要通过访问令牌颁发时提供的 refresh_token 刷新访问令牌,在这个例子中,我们使用Guzzle HTTP库来刷新令牌:

$http = new GuzzleHttp\Client;
$response = $http->post('http://your-app.com/oauth/token', [
'form_params' => [
'grant_type' => 'refresh_token',
'refresh_token' => 'the-refresh-token',
'client_id' => 'client-id',
'client_secret' => 'client-secret',
'scope' => '',
],
]);
return json_decode((string) $response->getBody(), true);

/oauth/token 路由会返回一个包含 access_token 、 refresh_token 和 expires_in 属性的JSON响应,同样, expires_in 属性包含访问令牌过期时间(s)。

5、密码发放令牌

OAuth2 密码发放允许你的其他第一方客户端,例如移动应用,使用邮箱地址/用户名+密码获取访问令牌。这使得你可以安全地颁发访问令牌给第一方客户端而不必要求你的用户走整个OAuth2授权码重定向流程。

创建一个密码发放客户端

在应用可以通过密码发放颁发令牌之前,需要创建一个密码发放客户端,你可以通过使用带 --password 选项的 passport:client 命令来实现。如果你已经运行了 passport:install 命令,则不必再运行这个命令:

php artisan passport:client --password 请求令牌

创建完密码发放客户端后,可以通过发送POST请求到 /oauth/token 路由(带上用户邮箱地址和密码参数)获取访问令牌。这个路由已经通过 Passport::routes 方法注册过了,不需要手动定义。如果请求成功,就可以从服务器返回的JSON响应中获取 access_token 和 refresh_token :

$http = new GuzzleHttp\Client;
$response = $http->post('http://your-app.com/oauth/token', [
'form_params' => [
'grant_type' => 'password',
'client_id' => 'client-id',
'client_secret' => 'client-secret',
'username' => 'taylor@laravel.com',
'password' => 'my-password',
'scope' => '',
],
]);
return json_decode((string) $response->getBody(), true);

注:记住,访问令牌默认长期有效,不过,如果需要的话你也可以配置访问令牌的最长生命周期。

请求所有域

使用密码发放的时候,你可能想要授权应用所支持的所有域的令牌,这可以通过请求 * 域来实现。如果你请求的是 * 域,则令牌实例上的 can 方法总是返回 true ,这个域只会分配给使用 password 发放的令牌:

$response = $http->post('http://your-app.com/oauth/token', [
'form_params' => [
'grant_type' => 'password',
'client_id' => 'client-id',
'username' => 'taylor@laravel.com',
'password' => 'my-password',
'scope' => '*',
],
]); 6、私人访问令牌

有时候,你的用户可能想要颁发访问令牌给自己而不走典型的授权码重定向流程。允许用户通过应用的UI颁发令牌给自己在用户体验你的API或者作为更简单的颁发访问令牌方式时会很有用。

注:私人访问令牌总是一直有效的,它们的生命周期在使用 tokensExpireIn 或 refreshTokensExpireIn 方法时不会修改。

创建一个私人访问客户端

在你的应用可以颁发私人访问令牌之前,需要创建一个私人的访问客户端。你可以通过带 --personal 选项的 passport:client 命令来实现,如果你已经运行过了 passport:install 命令,则不必再运行此命令:

php artisan passport:client --personal 管理私人访问令牌

创建好私人访问客户端之后,就可以使用 User 模型实例上的 createToken 方法为给定用户颁发令牌。 createToken 方法接收令牌名称作为第一个参数,以及一个可选的域数组作为第二个参数:

$user = App\User::find(1);
// Creating a token without scopes...
$token = $user->createToken('Token Name')->accessToken;
// Creating a token with scopes...
$token = $user->createToken('My Token', ['place-orders'])->accessToken; JSON API

Passport还提供了一个JSON API用于管理私人访问令牌,你可以将其与自己的前端配对以便为用户提供管理私人访问令牌的后台。下面,我们来回顾用于管理私人访问令牌的所有API。为了方便起见,我们使用Vue来演示发送HTTP请求到API。

注:如果你不想要实现自己的私人访问令牌前端,可以使用前端快速上手教程在数分钟内打造拥有完整功能的前端。

GET /oauth/scopes

这个路由会返回应用所定义的所有域。你可以使用这个路由来列出用户可以分配给私人访问令牌的所有域:

this.$http.get('/oauth/scopes')
.then(response => {
console.log(response.data);
});

GET /oauth/personal-access-tokens

这个路由会返回该认证用户所创建的所有私人访问令牌,这在列出用户的所有令牌以便编辑或删除时很有用:

this.$http.get('/oauth/personal-access-tokens')
.then(response => {
console.log(response.data);
});

POST /oauth/personal-access-tokens

这个路由会创建一个新的私人访问令牌,该路由要求传入两个参数:令牌的 name 和需要分配到这个令牌的 scopes :

const data = {
name: 'Token Name',
scopes: []
};
this.$http.post('/oauth/personal-access-tokens', data)
.then(response => {
console.log(response.data.accessToken);
})
.catch (response => {
// List errors on response...
});

DELETE /oauth/personal-access-tokens/{token-id}

这个路由可以用于删除私人访问令牌:

this.$http.delete('/oauth/personal-access-tokens/' + tokenId); 7、路由保护 通过中间件

Passport提供了一个认证guard用于验证输入请求的访问令牌,当你使用 passport 驱动配置好 api guard后,只需要在所有路由上指定需要传入有效访问令牌的 auth:api 中间件即可:

Route::get('/user', function () {
//
})->middleware('auth:api'); 传递访问令牌

调用被Passport保护的路由时,应用的API消费者需要在请求的 Authorization 头中指定它们的访问令牌作为 Bearer 令牌。例如:

$response = $client->request('GET', '/api/user', [
'headers' => [
'Accept' => 'application/json',
'Authorization' => 'Bearer '.$accessToken,
],

[ Laravel 5.3 文档 ] 安全 ―― API认证(Passport)保障安全性。的更多相关文章

  1. &lbrack; Laravel 5&period;5 文档 &rsqb; 处理用户请求 —— HTTP 请求的过滤器:中间件

    [ Laravel 5.5 文档 ] 处理用户请求 —— HTTP 请求的过滤器:中间件 http://laravelacademy.org/post/7812.html 简介 中间件为过滤进入应用的 ...

  2. 利用未文档化API:RtlGetNtVersionNumbers 获取系统版本号

    问题一:Windows SDK 8.1版本中的VersionHelper.h文件当中没有IsWindows10ORGreater,所以当你用IsWindows8Point1ORGreater判断出版本 ...

  3. 在线文档转换API word&comma;excel&comma;ppt等在线文件转pdf、png

    在线文档转换API提供word,excel,ppt等在线文件转pdf.png等,文档:https://www.juhe.cn/docs/api/id/259 接口地址:http://v.juhe.cn ...

  4. &lbrack; Laravel 5&period;5 文档 &rsqb; 快速入门 —— 目录结构篇

    简介 Laravel 默认的目录结构试图为不管是大型应用还是小型应用提供一个良好的起点.当然,你也可以按照自己的喜好重新组织应用的目录结构,因为 Laravel 对于指定类在何处被加载没有任何限制 — ...

  5. ABP文档 - Javascript Api - AJAX

    本节内容: AJAX操作相关问题 ABP的方式 AJAX 返回信息 处理错误 HTTP 状态码 WrapResult和DontWrapResult特性 Asp.net Mvc 控制器 Asp.net ...

  6. ABP文档 - Web Api 控制器

    文档目录 本节内容: 简介 AbpApiController 基类 本地化 其它 过滤 审计日志 授权 防伪造过滤 工作单元 结果包装和异常处理 结果缓存 验证 模块绑定器 简介 通过Abp.Web. ...

  7. ABP文档 - Javascript Api

    文档目录 本节内容: AJAX Notification Message UI Block & Busy Event Bus Logging Other Utility Functions A ...

  8. Laravel 5&period;1 文档攻略 —— Eloquent&colon;模型对象序列化

    在写api的时候,数据一般是以json格式进行传输的,没有对象可以直接使用.这个时候,对数据的序列化转换就很重要,Eloquent提供了很方便的方法和约定,不仅可以转换,还可以控制里面的键值. 基本用 ...

  9. 利用未文档化API:RtlAdjustPrivilege 提权实现自动关机

    这里主要是利用NTDLL.dll中未文档化的API: RtlAdjustPrivilege 来实现提权.自动关机的功能. RtlAdjustPrivilege定义如下: NTSTATUS RtlAdj ...

随机推荐

  1. 协议分析 - DHCP协议解码详解

    协议分析 - DHCP协议解码详解 [DHCP协议简介]         DHCP,全称是 Dynamic Host Configuration Protocol﹐中文名为动态主机配置协议,它的前身是 ...

  2. 通过代码获取res里生成R&period;java中的值

    引用:http://my.eoe.cn/blue_rain/archive/552.html 有的时候我们生成库文件,需要在里面加入一些UI,并提供出一些xml的资源,那如何在其他项目中使用呢? 我们 ...

  3. C&num;集合 -- 自定义集合与代理

    前面章节所讨论的集合都可以直接实例化,因此我们可以非常方便地使用这些集合类.但是如果你试图在集合添加或移除元素时添加控制,它们就不适用了.对于强类型集合,在某些情况下,你需要添加这样的控制: 添加或移 ...

  4. 设计模式之PHP项目应用——单例模式设计Memcache和Redis操作类

    1 单例模式简单介绍 单例模式是一种经常使用的软件设计模式. 在它的核心结构中仅仅包括一个被称为单例类的特殊类. 通过单例模式能够保证系统中一个类仅仅有一个实例并且该实例易于外界訪问.从而方便对实例个 ...

  5. BEGINNING SHAREPOINT&amp&semi;&num;174&semi; 2013 DEVELOPMENT 文件夹

    BEGINNING SHAREPOINT® 2013 DEVELOPMENT 文件夹 第一部分--開始使用SharePoint 2013 第1章节--SharePoint 2013 介绍 逐渐了解Sh ...

  6. QQ浏览器中时区bug

    在QQ浏览器 4.4.119.400 版本中,通过new Date('2018-11-11').getTime(); 获取的时间不是东8区的时间戳,而是0时区的时间戳,这就导致了获取的时间与实际的时间 ...

  7. Netty权威指南之Netty入门程序

    package com.hjp.netty.netty; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.Chan ...

  8. bam&sol;sam格式说明--转载

    在SAM输出的结果中每一行都包括十二项通过Tab分隔,从左到右分别是: 1 序列的名字(Read的名字) 2 概括出一个合适的标记,各个数字分别代表 1     序列是一对序列中的一个 2     比 ...

  9. Linux sudo 配置

    1.配置sudo权限 sudo配置信息保存在 /etc/sudoers 文件中,可以使用vi修改.但使用vi修改需要给root用户设置权限,可以使用 visudo 直接修改. visudo 复制这行将 ...

  10. win32之全屏窗口

    游戏开发中经常使用会让游戏以全屏窗口的状态运行,下面一个例子就是来实现这个效果的. #include <windows.h> void RegisterMyClass(); LRESULT ...