通过 Passport 实现 API 请求认证

准备工作

# 1.
$ composer require laravel/passport # 安装
$ php artisan migrate # 运行数据库迁移命令创建 OAuth 相关数据表
$ php artisan passport:install 

# 2.在对应模型类中使用 Laravel\Passport\HasApiTokens Trait
class User extends Authenticatable
{
    use HasApiTokens, Notifiable;
    ...
}

# 3.在 AuthServiceProvider 的 boot 方法中注册 API 认证相关路由
public function boot()
{
    ...
    Passport::routes();
}

# 4.修改配置文件config/auth.php
'guards' => [
    ...
    'api' => [
        'driver' => 'passport',
        'provider' => 'users',
    ],
],

OAuth 2.0 的四种方式

这里用 blog (后端)项目和新建的 testapp (前端)项目进行演示操作。

密码式 password

如果你高度信任某个应用,RFC 6749 也允许用户把用户名和密码,直接告诉该应用。该应用就使用你的密码,申请令牌,这种方式称为"密码式"(password)。

# 1. 在后端应用中注册,获取Client id 和 secret
# 2.配置 testapp 的 .env 和 config/services.php
# 3.在 testapp 中写控制器方法
# 4.测试后端应用授权移动端认证

# 1.在 blog 中运行命令注册 testapp ,获取到Client id 和 secret
$ php artisan passport:client --password

# 2. 
# 配置 testapp 的 .env 文件
CLIENT_ID=x
CLIENT_SECRET=xxx...
# 配置 config/services.php
'blog' => [
    'appid' => env('CLIENT_ID'),
    'secret' => env('CLIENT_SECRET'),
    'callback' => 'http://app.test/auth/callback'
]

# 3.
// 重写 AuthenticatesUsers 中的 login 方法
public function login(Request $request)
{
    $request->validate([
        'email' => 'required|string',
        'password' => 'required|string',
    ]);

    $http = new Client();
    // 发送相关字段到后端应用获取授权令牌
    $response = $http->post('http://blog.test/oauth/token', [
        'form_params' => [
            'grant_type' => 'password',
            'client_id' => config('services.blog.appid'),
            'client_secret' => config('services.blog.secret'),
            'username' => $request->input('email'),  // 这里传递的是邮箱
            'password' => $request->input('password'), // 传递密码信息
            'scope' => '*'
        ],
    ]);

    return response($response->getBody());
}

# 4.在浏览器访问http://app.test/login, 校验成功后就会返回令牌信息

授权码式 authorization code

授权码(authorization code)方式,指的是第三方应用先申请一个授权码,然后再用该码获取令牌。

# 1.在后端应用中注册,获取Client id 和 secret
# 2.配置 testapp 的 .env
# 3.编写 testapp 应用路由和控制器
# 4.测试通过授权码获取令牌

# 1.
$ php artisan passport:client
# What should we name the client?
$ testapp
# ...redirect...?
$ http://app.test/auth/callback

# 2. .env:
CLIENT_ID=x
CLIENT_SECRET=xxx...

# 3.
# web.php:
Route::get('/auth', 'Auth\LoginController@oauth');
Route::get('/auth/callback', 'Auth\LoginController@callback');
# LoginController.php:
public function oauth()
{

    $query = http_build_query([
        'client_id' => config('services.blog.appid'),
        'redirect_uri' => config('services.blog.callback'),
        'response_type' => 'code',
        'scope' => '',
    ]);

    return redirect('http://blog.test/oauth/authorize?'.$query);
}

public function callback(Request $request)
{
    $code = $request->get('code');
    if (!$code) {
        dd('授权失败');
    }
    $http = new Client();
    $response = $http->post('http://blog.test/oauth/token', [
        'form_params' => [
            'grant_type' => 'authorization_code',
            'client_id' => config('services.blog.appid'),  // your client id
            'client_secret' => config('services.blog.secret'),   // your client secret
            'redirect_uri' => config('services.blog.callback'),
            'code' => $code,
        ],
    ]);

    return response($response->getBody());
}

# 4.访问 http://app.test/auth,跳转授权,获取令牌

凭证式 client credentials

最后一种方式是凭证式(client credentials),适用于没有前端的命令行应用,即在命令行下请求令牌。

# 1 2 4 同上
# 3. web.php:
Route::get('/auth/client', 'Auth\LoginController@client');
LoginController.php:
public function client()
{
    $http = new Client();
    $response = $http->post('http://blog.test/oauth/token', [
        'form_params' => [
            'grant_type' => 'client_credentials',
            'client_id' => config('services.blog.appid'),  // your client id
            'client_secret' => config('services.blog.secret'),   // your client secret
            'scope' => '*'
        ],
    ]);

    return response($response->getBody());
}

沙箱测试篇(私人访问令牌)

这种授权方式比较特殊,不需要授权码,也不需要用户输入登录凭证,而是用户给自己颁发访问令牌。将后端应用 blog 类比做开放平台,在这个开发平台上通过的测试应用体验系统提供的认证 API。

# 1. 注册,获取 client id 和secret
$ php artisan passport:client --personal

# 2.在 User 模型类中使用HasApiTokens
class User extends Authenticatable
{
    use HasApiTokens, Notifiable;
    ...
}

# 3.
#在 routes/web.php 中定义一个路由用于测试获取访问令牌
Route::get('auth/personal', 'Auth\LoginController@personal');
# 控制器 LoginController 编写对应的方法 personal
public function personal()
{
    $user = User::where('name', 'Kong')->first();
    $token = $user->createToken('Users')->accessToken;
    dd($token);
}
# 4.访问

隐藏式 implicit

有些 Web 应用是纯前端应用,没有后端。这时就不能用上面的方式了,必须将令牌储存在前端。RFC 6749 就规定了第二种方式,允许直接向前端颁发令牌。这种方式没有授权码这个中间步骤,所以称为(授权码)"隐藏式"(implicit)。

# 在后端系统 AuthServiceProvider 的 boot 方法中调用 enableImplicitGrant 方法
public function boot()
{
    ...
Passport::enableImplicitGrant();
}

# 1 2 3 4 步骤同上

# 1.
$ php artisan passport:client
# ..name..?
$ testapp
# ..rediredt..?
$ http://app.test/auth/implicit/callback

# 2. .env不用我多说了吧
# 在 config/services.php 的 blog 配置项中修改 callback 配置值:
'callback' => 'http://app.test/auth/implicit/callback'

# 3.
# web.php
Route::get('/auth/implicit', 'Auth\LoginController@implicit');
Route::get('/auth/implicit/callback', 'Auth\LoginController@implicitCallback');
# LoginController.php
public function implicit()
{
    $query = http_build_query([
        'client_id' => config('services.blog.appid'),
        'redirect_uri' => config('services.blog.callback'),
        'response_type' => 'token',
        'scope' => '',
    ]);

    return redirect('http://blog.test/oauth/authorize?'.$query);
}

public function implicitCallback(Request $request)
{
    dd($request->get('access_token'));
}


令牌作用域

以授权码获取令牌为例。

# 后端
# 1.在 AuthServiceProvider.php 的 boot 方法中设置令牌作用域
# 2.在 app/Http/Kernel.php 的 $routeMiddleware 中引入两个中间件
# 3.在 routes/api.php 中新增路由
# 前端
# 4.修改授权码令牌对应配置的 scope

# 1. AuthServiceProvider.php:
Passport::tokensCan([
    'basic-user-info' => '获取用户名、邮箱信息',
    'all-user-info' => '获取用户所有信息',
    'get-post-info' => '获取文章详细信息',
]);

# 2.
'scopes' => \Laravel\Passport\Http\Middleware\CheckScopes::class,
'scope' => \Laravel\Passport\Http\Middleware\CheckForAnyScope::class,

# 3.
Route::middleware('auth:api')->group(function () {
    Route::get('/user', function (Request $request) {
        $user = $request->user();
        if ($user->tokenCan('all-user-info')) {
            // 如果用户令牌有获取所有信息权限,返回所有用户字段
            return $user;
        }
        // 否则返回用户名和邮箱等基本信息
        return ['name' => $user->name, 'email' => $user->email];
    })->middleware('scope:basic-user-info,all-user-info');
    Route::get('/post/{id}', function (Request $request, $id) {
        return \App\Post::find($id);
    })->middleware('scopes:get-post-info');
});

# 4.
public function oauth()
{

    $query = http_build_query([
        'client_id' => config('services.blog.appid'),
        'redirect_uri' => config('services.blog.callback'),
        'response_type' => 'code',
        'scope' => 'all-user-info get-post-info',
    ]);

    return redirect('http://blog.test/oauth/authorize?'.$query);
}

你可能感兴趣的:(通过 Passport 实现 API 请求认证)