Laravel Passport 优雅实现api登录功能(手机号/邮箱登录,验证码登录,第三方登录)

917 阅读1分钟

Laravel Passport 原文档地址:learnku.com/docs/larave…

新扩展包原教程地址:11.4. 处理第三方登录 | Oauth 认证 -- Passport |《L03 Laravel 教程 - 实战构架 API 服务器 ( Laravel 9.x ) 》| Laravel China 社区 (learnku.com)

介绍

Laravel Passport 可以在几分钟之内为你的应用程序提供完整的 OAuth2 服务端实现。Passport 是基于由 Andy Millington 和 Simon Hamp 维护的 League OAuth2 server 建立的。 除非您需要所有的oauth服务,否则还是更推荐您使用轻量级的Sanctum进行认证处理

Sanctum登录:juejin.cn/post/721654…

1、Laravel Passport扩展

  1. 安装扩展
composer required laravel/passport
  1. 执行迁移php artisan migrate
  2. 创建客户端php artisan passport:install(产出clint_id和client_secret要存好)
  3. 在User模型中加入trait(你必须确保该trait是Laravel\Passport\HasApiTokens
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Passport\HasApiTokens;

class User extends Authenticatable
{
    use HasApiTokens, HasFactory, Notifiable;
}
  1. 然后,在你应用的配置文件 config/auth.php中, 将 api 的授权看守器 guardsdriver 数的值设置为 passport 此调整会让你的应用程序使用 Passport 的 TokenGuard 鉴权 API 接口请求
'guards' => [
    'web' => [
        'driver' => 'session',
        'provider' => 'users',
    ],

    'api' => [
        'driver' => 'passport',
        'provider' => 'users',
    ],
],
  1. 发布配置文件
php artisan vendor:publish --tag=passport-config
  1. 如果你需要设置Token的生命周期,需要在 App\Providers\AuthServiceProvider 的 boot 方法中调用
/**
 * 注册身份验证/授权服务
 *
 * @return void
 */
public function boot()
{
    $this->registerPolicies();


    Passport::tokensExpireIn(now()->addDays(15));
    Passport::refreshTokensExpireIn(now()->addDays(30));
    Passport::personalAccessTokensExpireIn(now()->addMonths(6));
}
  1. 创建密码方式客户端
php artisan passport:client --password
  1. 配置用户名字段(兼容手机号邮箱)
/**
 * Find the user instance for the given username.
 */
public function findForPassport(string $username): User
{
    // 手机号或邮箱登录
    filter_var($username, FILTER_VALIDATE_EMAIL) ? $credentials['email'] = $username : $credentials['mobile'] = $username;

    return self::where($credentials)->first();
}
  1. 请求令牌

image.png 你的控制器要继承Laravel\Passport\Http\Controllers\AccessTokenController

<?php
namespace App\Http\Controllers;

use Illuminate\Http\Response;
use Laravel\Passport\Http\Controllers\AccessTokenController;
use Psr\Http\Message\ServerRequestInterface;

class LoginController extends AccessTokenController
{
    /**
     * 登录.
     *
     * @note   login
     * @author Kevin
     * @param  ServerRequestInterface $request
     * @return Response
     */
    public function login(ServerRequestInterface $request)
    {
        $getBody = $request->getParsedBody();
        // 配置作用域,不使用scope可传*或不传
        $scope   = request()->header('source', 'pc');
        $scopes  = [$scope];
        $newBody = array_merge(
            [
                'client_id'     => config('system.login.oauth_client.password.client_id'),
                'client_secret' => config('system.login.oauth_client.password.client_secret'),
                'scope'         => $scopes,
            ],
            $getBody
        );
        $newRequest = $request->withParsedBody($newBody);

        return $this->issueToken($newRequest)->setStatusCode(200);
    }
}
  1. 如果你需要使用作用域:

设置作用域

你可以在 App\Providers\AuthServiceProvider 的 boot 方法中使用 Passport::tokensCan 方法来定义 API 的作用域

/**
 * 注册身份验证/授权服务。
 *
 * @return void
 */
public function boot()
{
    $this->registerPolicies();


    Passport::tokensCan([
        'place-orders' => 'Place orders',
        'check-status' => 'Check order status',
    ]);
}

应用作用域

检查作用域 Passport 包含两个中间件,可用于验证传入的请求是否包含访问指定作用域的令牌。 使用之前,需要将下面的中间件添加到 app/Http/Kernel.php 文件的 $routeMiddleware 属性中:

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

检查所有作用域 路由可以使用 scopes 中间件来检查当前请求是否拥有指定的 所有 作用域:

Route::get('/orders', function () {
    // 访问令牌具有 "check-status" 和 "place-orders" 作用域...
})->middleware(['auth:api', 'scopes:check-status,place-orders']);

检查任意作用域 路由可以使用 scope 中间件来检查当前请求是否拥有指定的 任意 作用域:

Route::get('/orders', function () {
    // 访问令牌具有 "check-status" 或 "place-orders" 作用域...
})->middleware(['auth:api', 'scope:check-status,place-orders']);
  1. 路由保护 通过中间件, Passport 包含一个 验证保护机制 验证请求中传入的访问令牌。 若配置 api 的看守器使用 passport 驱动,你只要在需要有效访问令牌的路由上指定 auth:api 中间件即可
Route::get('/user', function () { 
//
})->middleware('auth:api');

2、教程包kslimani/laravel-passport-grant(扩展登录方式)

  1. 安装扩展
composer require kslimani/laravel-passport-grant
  1. 发布配置文件
php artisan vendor:publish --provider="Sk\Passport\GrantTypesServiceProvider" --tag="config"
  1. 使用扩展(在App/Passport下创建文件进行关联)

image.png

这样就会根据登录接口grant_type参数值匹配provider进行执行,可在provider中进行逻辑处理,登录成功与否取决于此文件返回值是null还是一个User模型,例如;

<?php

declare(strict_types=1);

namespace App\Passport;

use App\Models\User;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Support\Facades\Hash;
use Psr\Http\Message\ServerRequestInterface;
use Sk\Passport\UserProvider;

class PasswordUserProvider extends UserProvider
{
    public function validate(ServerRequestInterface $request)
    {
        $this->validateRequest($request, [
            'username' => 'required|string',
            'password' => 'required|string',
        ]);
    }

    /**
     * 操作.
     *
     * @note   retrieve
     * @author Kevin
     *
     * @param  ServerRequestInterface  $request
     * @return null|mixed|User
     * @throws AuthenticationException
     */
    public function retrieve(ServerRequestInterface $request)
    {
        $inputs = $this->only($request, [
            'username',
            'password',
        ]);

        try {
            $user = app(User::class)->findForPassport($inputs['username']);

            if (is_null($user) || !Hash::check($inputs['password'], $user->password)) {
                throw new AuthenticationException('账号或密码输入有误');
            }

            return $user;
        } catch (AuthenticationException $exception) {
            // log

            throw new AuthenticationException($exception->getMessage());
        }
    }
}

结果展示

image.png