composer require laravel/passport
php artisan migrate // 创建表来存储客户端和 access_token
php artisan passport:install // 生成加密 access_token 的 key、密码授权客户端、个人访问客户端
Laravel\Passport\HasApiTokens Trait 添加到 App\User 模型中 // 提供一些辅助函数检查已认证用户的令牌和使用范围
在 AuthServiceProvider 的 boot 方法中调用 Passport::routes 函数 // 访问令牌并撤销访问令牌路由,客户端和个人访问令牌相关路由
config/auth.php 中 api 的 driver 选项改为 passport
php artisan vendor:publish --tag=passport-migrations
php artisan passport:keys
Passport::loadKeysFrom('/secret-keys/oauth');
PASSPORT_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
<private key here>
-----END RSA PRIVATE KEY-----"
PASSPORT_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----
<public key here>
-----END PUBLIC KEY-----"
Passport::tokensExpireIn(now()->addDays(15)); // access_token
Passport::refreshTokensExpireIn(now()->addDays(30));// refresh_token
Passport::personalAccessTokensExpireIn(now()->addMonths(6)); // personal access_token
use App\Models\Passport\AuthCode;
use App\Models\Passport\Client;
use App\Models\Passport\PersonalAccessClient;
use App\Models\Passport\Token;
/**
* Register any authentication / authorization services.
*
* @return void
*/
public function boot()
{
$this->registerPolicies();
Passport::routes();
Passport::useTokenModel(Token::class);
Passport::useClientModel(Client::class);
Passport::useAuthCodeModel(AuthCode::class);
Passport::usePersonalAccessClientModel(PersonalAccessClient::class);
}
命令行创建客户端
php artisan passport:client
// 设置回调地址白名单的格式:http://example.com/callback,http://examplefoo.com/callback (逗号隔开)
api 管理客户端
axios.get('/oauth/clients')
.then(response => {
console.log(response.data);
});
const data = {
name: 'Client Name',
redirect: 'http://example.com/callback'
};
axios.post('/oauth/clients', data)
.then(response => {
console.log(response.data);
})
.catch (response => {
// List errors on response...
});
const data = {
name: 'New Client Name',
redirect: 'http://example.com/callback'
};
axios.put('/oauth/clients/' + clientId, data)
.then(response => {
console.log(response.data);
})
.catch (response => {
// List errors on response...
});
axios.delete('/oauth/clients/' + clientId)
.then(response => {
//
});
请求 token
Route::get('/redirect', function (Request $request) {
$request->session()->put('state', $state = Str::random(40));
$query = http_build_query([
'client_id' => 'client-id',
'redirect_uri' => 'http://example.com/callback',
'response_type' => 'code',
'scope' => '',
'state' => $state,
]);
return redirect('http://your-app.com/oauth/authorize?'.$query);
});
自定义用户授权页面
php artisan vendor:publish --tag=passport-views
跳过用户授权页面
<?php
namespace App\Models\Passport;
use Laravel\Passport\Client as BaseClient;
class Client extends BaseClient
{
public function skipsAuthorization()
{
return $this->firstParty();
}
}
获取 access_token
Route::get('/callback', function (Request $request) {
$state = $request->session()->pull('state');
throw_unless(
strlen($state) > 0 && $state === $request->state,
InvalidArgumentException::class
);
$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);
});
刷新令牌
$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);
php artisan passport:client --password
$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);
自定义密码验证和 username 字段
public function validateForPassportPasswordGrant($password)
{
return Hash::check($password, $this->password);
}
public function findForPassport($username)
{
return $this->where('username', $username)->first();
}
Passport::enableImplicitGrant();
Route::get('/redirect', function (Request $request) {
$request->session()->put('state', $state = Str::random(40));
$query = http_build_query([
'client_id' => 'client-id',
'redirect_uri' => 'http://example.com/callback',
'response_type' => 'token',
'scope' => '',
'state' => $state,
]);
return redirect('http://your-app.com/oauth/authorize?'.$query);
});
php artisan passport:client --client
use Laravel\Passport\Http\Middleware\CheckClientCredentials;
protected $routeMiddleware = [
'client' => CheckClientCredentials::class,
];
Route::get('/orders', function (Request $request) {
...
})->middleware('client');
Route::get('/orders', function (Request $request) {
...
})->middleware('client:check-status,your-scope');
$guzzle = new GuzzleHttp\Client;
$response = $guzzle->post('http://your-app.com/oauth/token', [
'form_params' => [
'grant_type' => 'client_credentials',
'client_id' => 'client-id',
'client_secret' => 'client-secret',
'scope' => 'your-scope',
],
]);
return json_decode((string) $response->getBody(), true)['access_token'];
$response = $client->request('GET', '/api/user', [
'headers' => [
'Accept' => 'application/json',
'Authorization' => 'Bearer '.$accessToken,
],
]);
# AuthServiceProvider
use Laravel\Passport\Passport;
Passport::tokensCan([
'place-orders' => 'Place orders',
'check-status' => 'Check order status',
]);
Passport::setDefaultScope([
'check-status',
'place-orders',
]);
Route::get('/redirect', function () {
$query = http_build_query([
'client_id' => 'client-id',
'redirect_uri' => 'http://example.com/callback',
'response_type' => 'code',
'scope' => 'place-orders check-status', // 传递 scope 格式
]);
return redirect('http://your-app.com/oauth/authorize?'.$query);
});
检验 scope
# app/Http/Kernel.php 中 $routeMiddleware
'scopes' => \Laravel\Passport\Http\Middleware\CheckScopes::class,
'scope' => \Laravel\Passport\Http\Middleware\CheckForAnyScope::class,
Route::get('/orders', function () {
// Access token has both "check-status" and "place-orders" scopes...
})->middleware('scopes:check-status,place-orders');
Route::get('/orders', function () {
// Access token has either "check-status" or "place-orders" scope...
})->middleware('scope:check-status,place-orders');
就算含有访问令牌验证的请求已经通过应用程序的验证,你仍然可以使用当前授权 User
实例上的 tokenCan
方法来验证令牌是否拥有指定的作用域
use Illuminate\Http\Request;
Route::get('/orders', function (Request $request) {
if ($request->user()->tokenCan('place-orders')) {
//
}
});
scopeIds
方法将返回所有已定义 ID / 名称的数组:
Laravel\Passport\Passport::scopeIds();
scopes
方法将返回一个包含所有已定义作用域数组的 Laravel\Passport\Scope
实例:
Laravel\Passport\Passport::scopes();
scopesFor
方法将返回与给定 ID / 名称匹配的 Laravel\Passport\Scope
实例数组:
Laravel\Passport\Passport::scopesFor(['place-orders', 'check-status']);
你可以使用 hasScope
方法确定是否已定义给定作用域:
Laravel\Passport\Passport::hasScope('place-orders');
protected $listen = [
'Laravel\Passport\Events\AccessTokenCreated' => [
'App\Listeners\RevokeOldTokens',
],
'Laravel\Passport\Events\RefreshTokenCreated' => [
'App\Listeners\PruneOldTokens',
],
];
'web' => [
// Other middleware...
\Laravel\Passport\Http\Middleware\CreateFreshApiToken::class,
],
// 注意:你应该确保在您的中间件堆栈中 CreateFreshApiToken 中间件之前列出了 EncryptCookies 中间件。
axios.get('/api/user')
.then(response => {
console.log(response.data);
});
自定义 Cookie 名称
public function boot()
{
$this->registerPolicies();
Passport::routes();
Passport::cookie('custom_name');
}
actingAs 方法可以指定当前已认证用户及其作用域 。
use App\User;
use Laravel\Passport\Passport;
public function testServerCreation()
{
Passport::actingAs(
factory(User::class)->create(),
['create-servers']
);
$response = $this->post('/api/create-server');
$response->assertStatus(201);
}
actingAsClient 方法可以指定当前已认证客户端及其作用域 。
use Laravel\Passport\Client;
use Laravel\Passport\Passport;
public function testGetOrders()
{
Passport::actingAsClient(
factory(Client::class)->create(),
['check-status']
);
$response = $this->get('/api/orders');
$response->assertStatus(200);
}