Menu

2.7. API 认证

在返回响应之前,大多数 API 需要通过某种形式的身份验证。 有时,经过和未经过身份验证的请求,响应的内容会有所不同。

这个软件包允许您配置多个身份验证提供者。 当启用身份验证后,每一个提供者都会尝试对请求进行身份验证。

配置身份验证提供者

默认情况下,仅在配置文件中启用 HTTP 基本身份验证。以下是该包内置支持身份验证提供者的列表:

  • HTTP Basic (Dingo\Api\Auth\Provider\Basic)
  • JSON Web Tokens (Dingo\Api\Auth\Provider\JWT)
  • OAuth 2.0 (Dingo\Api\Auth\Provider\OAuth2)

HTTP 基本验证

这个服务提供者使用 laravellumen 中内置的默认基本身份验证。 您需要在配置文件或者引导文件中配置此服务提供者,第二个参数用于认证的标识符。

app('Dingo\Api\Auth\Auth')->extend('basic', function ($app) {
   return new Dingo\Api\Auth\Provider\Basic($app['auth'], 'email');
});

JSON Web Tokens (JWT)

tymon/jwt-auth 是使用第三方来集成 JWT 身份验证的软件包。 有关安装和配置的详细信息,请参阅 GitHub 页面。

一旦您安装了这个软件包,您就可以在 config/api.php 文件或者引导文件中配置该服务提供者。

'auth' => [
    'jwt' => 'Dingo\Api\Auth\Provider\JWT',
],
app('Dingo\Api\Auth\Auth')->extend('jwt', function ($app) {
   return new Dingo\Api\Auth\Provider\JWT($app['Tymon\JWTAuth\JWTAuth']);
});

OAuth 2.0

这个包是使用第三方来集成的 OAuth 2.0 。 您可以安装 league/oauth2-server 并配置服务提供者,或者,您也可以使用 lucadegasperi/oauth2-server-laravel

为了简单起见,以下例子将使用 lucadegasperi/oauth2-server-laravel 这个 Laravel 定制包。

一旦您安装了这个包,您需要配置 provider 或者 bootstrap 文件。

app('Dingo\Api\Auth\Auth')->extend('oauth', function ($app) {
   $provider = new Dingo\Api\Auth\Provider\OAuth2($app['oauth2-server.authorizer']->getChecker());

    $provider->setUserResolver(function ($id) {
        // 获取用户 ID 的代码逻辑
    });

    $provider->setClientResolver(function ($id) {
        // 通过 Client ID 获取 OAuth Client 的逻辑
    });

    return $provider;
});

或者配置服务提供者。

namespace App\Providers;

use Dingo\Api\Auth\Auth;
use Dingo\Api\Auth\Provider\OAuth2;
use Illuminate\Support\ServiceProvider;

class OAuthServiceProvider extends ServiceProvider
{
    public function boot()
    {
        $this->app[Auth::class]->extend('oauth', function ($app) {
            $provider = new OAuth2($app['oauth2-server.authorizer']->getChecker());

            $provider->setUserResolver(function ($id) {
                // 获取用户 ID 的代码逻辑
            });

            $provider->setClientResolver(function ($id) {
                // 通过 Client ID 获取 OAuth Client 的逻辑
            });

            return $provider;
        });
    }

    public function register()
    {
        //
    }
}

用户和客户端解析器

根据授权许可,您可能不需要2个解析器。例如:如果您只需要客户端通过 OAuth 2.0 身份验证,则不需要配置用户解析器。

解析器都接收用户或者客户端的 ID,并用这个 ID 返回用户或者客户端的实例。这通常涉及用户或者客户端的数据库查询。

自定义身份验证

如果您正在维护以前的系统或者需要其他形式的身份验证,则可以实施自定义身份验证。

您的身份验证提供者应该实现 Dingo\Api\Contract\Auth\Provider 。如果通过身份验证,身份验证提供者应该返回该用户的实例, 如果身份验证失败,则抛出一个 Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException 异常

use Illuminate\Http\Request;
use Dingo\Api\Routing\Route;
use Dingo\Api\Contract\Auth\Provider;
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;

class CustomProvider implements Provider
{
    public function authenticate(Request $request, Route $route)
    {
        // 对请求进行验证的逻辑

        throw new UnauthorizedHttpException('Unable to authenticate with supplied username and password.');
    }
}

Dingo\Api\Auth\Provider\Authorization 将颁发一个带 Authorization 头信息的 tokenDingo\Api\Auth\Provider\Authorization::validateAuthorizationHeader 可以轻松的验证 Authorization 头信息是否存在,并且是有效的。


use Illuminate\Http\Request;
use Dingo\Api\Routing\Route;
use Dingo\Api\Auth\Provider\Authorization;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;

class CustomProvider extends Authorization
{
    public function authenticate(Request $request, Route $route)
    {
        $this->validateAuthorizationHeader($request);

        // 如果  Authorization  通过验证,我们可以继续进行身份验证
        // 如果  Authorization  验证失败, 我们必须抛出一个  UnauthorizedHttpException  的异常。
    }

    public function getAuthorizationMethod()
    {
        return 'mac';
    }
}

一旦您实现了自己的身份验证提供者,您需要在 config/api.php 这个文件进行配置。

'auth' => [
    'custom' => 'CustomProvider',
],

或者,配置 bootstrapprovider 文件。

app('Dingo\Api\Auth\Auth')->extend('custom', function ($app) {
    return new CustomProvider;
});

保护级别

您可以通过 api.auth 路由中间件来启用路由或者路由群组的保护。

如果您使用 Laravel 的定制包。则可以直接使用 api.auth 这个中间件。并且不需要注册。

在所有的路由上启用

$api->version('v1', ['middleware' => 'api.auth'], function ($api) {
    // 在这个版本群组下的所有路由将进行身份验证。
});

特定的路由上启用

$api->version('v1', function ($api) {
    $api->get('user', ['middleware' => 'api.auth', function () {
        // 这个路由将进行身份验证。
    }]);

    $api->get('posts', function () {
        // 这个路由不会验证身份。
    });
});

只允许特定的身份验证提供者

如果你想在一个群组或者特定的路由上验证身份验证提供者。你可以启用 providers 来配置。

$api->version('v1', function ($api) {
    $api->get('user', ['middleware' => 'api.auth', 'providers' => ['basic', 'oauth'], function () {
        // 这个路由将进行身份验证。
    }]);
});

控制器上进行身份验证

LaravelLumen 都可以在控制器里启用中间件。您可以在构造函数里使用 middleware 的方法。

class UserController extends Illuminate\Routing\Controller
{
    use Helpers;

    public function __construct()
    {
        $this->middleware('api.auth');

        // 这个中间件只在  index  中启用
        $this->middleware('api.auth', ['only' => ['index']]);
    }

    public function index()
    {
        //
    }

    public function posts()
    {
        //
    }
}

检索经过身份验证的用户

你可以在受保护的接口里检索已通过身份验证的用户

$api->version('v1', ['middleware' => 'api.auth'], function ($api) {
    $api->get('user', function () {
        $user = app('Dingo\Api\Auth\Auth')->user();

        return $user;
    });
});

如果你的控制器里使用了 Dingo\Api\Routing\Helpers 那么你就可以使用 $auth

use Dingo\Api\Routing\Helpers;
use Illuminate\Routing\Controller;

class UserController extends Controller
{
    use Helpers;

    public function __construct()
    {
        $this->middleware('api.auth');
    }

    public function index()
    {
        $user = $this->auth->user();

        return $user;
    }
}

可选的验证

有时你可能需要根据请求是否通过身份验证来修改响应值,为此,路由不应该设置中间件去保护,你只需要手动认证用户。

$api->version('v1', function ($api) {
    $api->get('users/{id}', function ($id) {
        $user = User::findOrFail($id);

        // 尝试验证请求。如果请求未被认证那么我们会从响应中隐藏电子邮件。只认证
                // 请求可以看到其他用户的电子邮件。

        if (! app('Dingo\Api\Auth\Auth')->user()) {
            $hidden = $user->getHidden();

            $user->setHidden(array_merge($hidden, ['email']));
        }

        return $user;
    });
});

本文章首发在 Laravel China 社区
上一篇 下一篇
讨论数量: 0
发起讨论


暂无话题~
刻意练习,每日精进。
7
点赞
3455
浏览
0
讨论
译者