Laravel5.3 Passport API 认证 密码模式使用

laravel5.3 Passport API 认证 密码模式使用#

首先根据文档先安装好这个 passport
使用 Composer 包管理器安装

composer require laravel/passport
===================================
{
"name": "laravel/laravel",
"description": "The Laravel Framework.",
"keywords": ["framework", "laravel"],
"license": "MIT",
"type": "project",
"require": {
    "php": ">=5.6.4",
    "laravel/framework": "5.3.*",
    "barryvdh/laravel-ide-helper": "^2.2",
    "laravel/passport": "^1.0"
},

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

'providers' => [
     Laravel\Passport\PassportServiceProvider::class,
],

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

# 在vendor/laravel/passport/database/migrations/目录下会有五个文件
2016_06_01_000001_create_oauth_auth_codes_table.php
2016_06_01_000001_create_oauth_auth_access_tokens_table.php
2016_06_01_000001_create_oauth_auth_refresh_tokens_table.php
2016_06_01_000001_create_oauth_auth_clients_table.php
2016_06_01_000001_create_oauth_auth_personal_access_clients_table.php

执行 php artisan migrate 迁移数据表 数据库会生成五张表

运行 php artisan passport:install

[root@bogon shop]# php artisan passport:install
Encryption keys generated successfully.
Personal access client created successfully.
Client ID: 1
Client Secret: tOcvJtXKhYSSQCNCac88XNzaQaeB2jrakiQwnurn
Password grant client created successfully.
Client ID: 2
Client Secret: Hxa47zrBhmXksaEBTKpGyhi1dNTVWPwCFY8jwFkQ

该命令会在 storage 目录中生成认证需要的加密键

oauth-private.key
oauth-public.key

同时会创建 'personal access' 和 'password grant' 客户端用于生成访问令牌,存在 oauth_clients 表中

运行完这个命令后,添加 Laravel\Passport\HasApiToken sttaitApp\User 模型

namespace App;

use Laravel\Passport\HasApiTokens;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    use HasApiTokens, Notifiable;

该 trait 关联了认证相关的一些模型,可以用获取认证过的客户端,用户的 token

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

namespace App\Providers;

use Laravel\Passport\Passport;
use Illuminate\Support\Facades\Gate;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;

class AuthServiceProvider extends ServiceProvider
{

public function boot()
    {
        $this->registerPolicies();

        Passport::routes();
        //
    }

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

'api' => [
        //'driver' => 'token',
        'driver' => 'passport',
        'provider' => 'users',
    ],

安装官方文档,就对 passport 的配置已经完成。接下来就是在自己的项目中如何应用密码授权模式。

首先,需要创建一个密码发放客户端,在配置中,已经通过运行 php artisan passport:install 命令,创建了一个密码令牌的客户端

客户端请求令牌#

将创建完的密码给客户端,然后客户端可以通过发送 POST 请求到 /oauth/token 路由(带上用户邮箱地址和密码参数)获取访问令牌。

路由在 AuthServiceProvider.php 中通过 Passport::routes 方法,调用了安装的 passport 包中的 Passport 类中的 routes 方法,

打开文件 \Laravel\Passport\Passport 的 routes 方法#

public static function routes($callback = null, array $options = [])
{
    $callback = $callback ?: function ($router) {
    $router->all();
};

#点击all方法跳转到\Laravel\Passport\RouteRegistrar的all方法
public function all()
{
        $this->forAuthorization();
        $this->forAccessTokens();
        $this->forTransientTokens();
        $this->forClients();
        $this->forPersonalAccessTokens();
}

#走的是第二个方法点击跳到\Laravel\Passport\RouteRegistrar的forAccessTokens方法
public function forAccessTokens()
{
        $this->router->post('/oauth/token', [
                'uses' => 'AccessTokenController@issueToken',
                'middleware' => 'throttle'
        ]);

        $this->router->group(['middleware' => ['web', 'auth']], function ($router) {
                $router->get('/oauth/tokens', [
                        'uses' => 'AuthorizedAccessTokenController@forUser',
                ]);

                $router->delete('/oauth/tokens/{token_id}', [
                        'uses' => 'AuthorizedAccessTokenController@destroy',
                ]);
        });
}

#看上面的post方法找到对应的控制器
\Laravel\Passport\Http\Controller\AccessTokenController > issueToken方法
public function issueToken(ServerRequestInterface $request)
{
        return $this->withErrorHandling(function () use ($request) {
                return $this->server->respondToAccessTokenRequest($request, new Psr7Response);
        });
}

#继续跟进代码,找到处理请求参数的逻辑
#进入到\Legaue\OAuth2\Server\AuthorizationServer > respondToAccessTokenRequest方法
public function respondToAccessTokenRequest(ServerRequestInterface $request, ResponseInterface $response)
{
        foreach ($this->enabledGrantTypes as $grantType) {
                if ($grantType->canRespondToAccessTokenRequest($request)) {
                        $tokenResponse = $grantType->respondToAccessTokenRequest(
                                $request,
                                $this->getResponseType(),
                                $this->grantTypeAccessTokenTTL[$grantType->getIdentifier()]
                        );

                        if ($tokenResponse instanceof ResponseTypeInterface) {
                                return $tokenResponse->generateHttpResponse($response);
                        }
                }
        }

        可以打印出来$this->enabledGrantTypes,里面有五个值,对应不同的认证方式我们使用的是密码模式对应的就是PasswordGrant.php文件
        public function respondToAccessTokenRequest(
        ServerRequestInterface $request,
        ResponseTypeInterface $responseType,
        \DateInterval $accessTokenTTL
) {
        // Validate request
        $client = $this->validateClient($request);
        $scopes = $this->validateScopes($this->getRequestParameter('scope', $request));
        $user = $this->validateUser($request, $client);
        // Finalize the requested scopes
        $scopes = $this->scopeRepository->finalizeScopes($scopes, $this->getIdentifier(), $client, $user->getIdentifier());

        // Issue and persist new tokens
        $accessToken = $this->issueAccessToken($accessTokenTTL, $client, $user->getIdentifier(), $scopes);
        $refreshToken = $this->issueRefreshToken($accessToken);

        // Inject tokens into response
        $responseType->setAccessToken($accessToken);
        $responseType->setRefreshToken($refreshToken);

        return $responseType;
}

#这个方法中首先通过validateClient方法,检验客户端,获取请求头中的clientId和clientSecret,通过clientId查表oauth_clients,如果存在,则将id和表中的客户端名字以及redirect路径传到Client类中并实例化得到对象复制给$client。然后调用validateUser方法校验请求的用户是否存在。查看ValidateUser方法
protected function validateUser(ServerRequestInterface $request, ClientEntityInterface $client)
{
        $username = $this->getRequestParameter('username', $request);

        if (is_null($username)) {
                throw OAuthServerException::invalidRequest('username');
        }

        $password = $this->getRequestParameter('password', $request);
        if (is_null($password)) {
                throw OAuthServerException::invalidRequest('password');
        }

        $user = $this->userRepository->getUserEntityByUserCredentials(
                $username,
                $password,
                $this->getIdentifier(),
                $client
        );

#打开\Laravel\Passport\Birdge\UserRepository的getUserEntityByUserCredentials方法
public function getUserEntityByUserCredentials($username, $password, $grantType, ClientEntityInterface $clientEntity)
{
        if (is_null($model = config('auth.providers.users.model'))) {
                throw new RuntimeException('Unable to determine user model from configuration.');
        }
        if (method_exists($model, 'findForPassport')) {
                $user = (new $model)->findForPassport($username);
        } else {
                $user = (new $model)->where('email', $username)->first();
        }

        if (! $user ) {
                return;
        } elseif (method_exists($user, 'validateForPassportPasswordGrant')) {
                if (! $user->validateForPassportPasswordGrant($password)) {
                        return;
                }
        } elseif (! $this->hasher->check($password, $user->password)) {
                return;
        }

        return new User($user->getAuthIdentifier());
}

这里是来检测是 App\User 模型来效验用户名和密码
然后就可以使用 postman 来做一个简单的请求测试
请求参数
username users 表的 username 默认是 email 字段
password users 表的 password
grant_type 类型是 password
client_id 数据库生成的 id
client_secret 数据库 id 对应的 secret

本作品采用《CC 协议》,转载必须注明作者和本文链接
994914376
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。