ridibooks/oauth2

Ridibooks OAuth2

v0.3.1 2020-01-06 01:41 UTC

This package is auto-updated.

Last update: 2024-09-04 13:48:29 UTC


README

Build Status

介绍

  • 这是一个用于构建OAuth2客户端和资源服务器的PHP库。
  • 按照Ridi风格指南(内部服务间SSO)编写。
  • 可选择支持JWK缓存。通过向JwtTokenValidator注入psr-6的实现体,可以使用缓存功能。

需求

  • PHP 7.2或更高版本
  • 为了使用php7.2-gmp web-token解密模块,需要在操作系统内安装php7.2-gmp。因此,请务必在库客户端的操作系统或Docker镜像内安装。请参考PR
  • silex/silex v1.3.x(可选)
  • symfony/symfony v4.x.x(可选)
  • guzzlehttp/guzzle(可选)

安装

composer require ridibooks/oauth2

使用

无缓存JwtTokenValidator

use Ridibooks\OAuth2\Authorization\Validator\JwtTokenValidator;

$access_token = '...';

try {
    $jwk_url = $this->configs['jwk_url'];
    $validator = new JwtTokenValidator($jwk_url);
    $validator->validateToken($access_token);
} catch (AuthorizationException $e) {
	// handle exception
}

带缓存JwtTokenValidator

use Ridibooks\OAuth2\Authorization\Validator\JwtTokenValidator;

$access_token = '...';

try {
    $jwk_url = $this->configs['jwk_url'];
    $cache_item_pool = new FilesystemAdapter(); // [psr-6](https://www.php-fig.org/psr/psr-6/) Implementation Adaptor
    $validator = new JwtTokenValidator($jwk_url, $cache_item_pool);
    $validator->validateToken($access_token);
} catch (AuthorizationException $e) {
	// handle exception
}

范围检查器

$required = ['write', 'read'];
if (ScopeChecker::every($required, $granted)) {
	// pass
}

授权者

$client_info = new ClientInfo('client_id', 'client_secret', ['scope'], 'redirect_uri');
$auth_server_info = new AuthorizationServerInfo('authorization_url', 'token_url');

$granter = new Granter($client_info, $auth_server_info);
$authorization_url = $granter->authorize();
// Redirect to `$authorization_url`

使用:与Silex提供者一起使用

OAuth2ServiceProvider注册到Silex应用程序中(使用register方法)。

服务

  • OAuth2ProviderKeyConstant::GRANTER
    • authorize(string $state, string $redirect_uri = null, array $scope = null): string:返回用于/authorize的URL
  • OAuth2ProviderKeyConstant::AUTHORIZER
    • autorize(Request $request): JwtToken:在检查access_token的有效性后返回JwtToken对象
  • OAuth2ProviderKeyConstant::MIDDLEWARE
    • authorize(OAuth2ExceptionHandlerInterface $exception_handler = null, UserProviderInterface $user_provider = null, array $required_scopes = []):返回中间件

示例:OAuth2ProviderKeyConstant::MIDDLEWARE服务

use Ridibooks\OAuth2\Silex\Constant\OAuth2ProviderKeyConstant as KeyConstant;
use Ridibooks\OAuth2\Silex\Handler\LoginRequiredExceptionHandler;
use Ridibooks\OAuth2\Silex\Provider\OAuth2ServiceProvider;
use Ridibooks\OAuth2\Authorization\Validator\JwtTokenValidator;
use Example\UserProvder;

// `OAuth2ServiceProvider` 등록
$app->register(new OAuth2ServiceProvider(), [
	KeyConstant::CLIENT_ID => 'example-client-id',
	KeyConstant::CLIENT_SECRET => 'example-client-secret',
	KeyConstant::JWT_VALIDATOR => new JwtTokenValidator($jwk_url)
]);

// 미들웨어 등록
$app->get('/auth-required', [$this, 'authRequiredApi'])
	->before($app[KeyConstant::MIDDLEWARE]->authorize(new LoginRequiredExceptionHandler(), new UserProvider());
	
public function authRequiredApi(Application $app)
{
	// 사용자 추출
	$user = $app[KeyConstant::USER];
	...
}

示例:OAuth2ProviderKeyConstant::AUTHORIZER服务

use Ridibooks\OAuth2\Authorization\Authorizer;
use Ridibooks\OAuth2\Authorization\Exception\AuthorizationException;
use Ridibooks\OAuth2\Silex\Constant\OAuth2ProviderKeyConstant;
use Ridibooks\OAuth2\Silex\Provider\OAuth2ServiceProvider;
use Silex\Application;
use Symfony\Component\HttpFoundation\Request;
use Ridibooks\OAuth2\Authorization\Validator\JwtTokenValidator;

...

// `OAuth2ServiceProvider` 등록
$app->register(new OAuth2ServiceProvider(), [
	KeyConstant::CLIENT_ID => 'example-client-id',
	KeyConstant::CLIENT_SECRET => 'example-client-secret',
    KeyConstant::JWT_VALIDATOR => new JwtTokenValidator($jwk_url)
]);

...

$app->get('/', function (Application $app, Request $request) {
	/** @var Authorizer $authorizer */
	$authorizer = $app[OAuth2ProviderKeyConstant::AUTHORIZER];
	try {
		$token = $authorizer->authorize($request);
		return $token->getSubject();
	} catch (AuthorizationException $e) {
		// handle authorization error ...
	}
});

使用:与Symfony包一起使用

服务

  • Granter()
    • OAuth2ServiceProvider::getGranter()
    • Granter::authorize(string $state, string $redirect_uri = null, array $scope = null): string:返回用于/authorize的URL
  • Authorizer()
    • OAuth2ServiceProvider::getAuthorizer()
    • Authorizer::autorize(Request $request): JwtToken:在检查access_token的有效性后返回JwtToken对象
  • OAuth2Middleware
    • OAuth2ServiceProvider::getMiddleware()
    • 在创建OAuth2ServiceProvider时,作为Symfony事件订阅者进行注册

示例:OAuth2Middleware服务

配置

  • 配置示例可以在tests/Symfony中查看。
1. 注册OAuth2ServiceProviderBundle
# example: <project_root>/config/bundles.php

return [
    ...,
    Ridibooks\OAuth2\Symfony\OAuth2ServiceProviderBundle::class => ['all' => true]
];
2. 参数和服务设置
  • 可以使用'%env(VARIABLE)%'来使用环境变量。
  • 必需
    • client_id
    • client_secret
    • authorize_url
    • token_url
    • jwk_url
    • user_info_url
    • token_cookie_domain
    • default_exception_handler
  • 可选
    • client_default_scope
    • client_default_redirect_uri
    • default_user_provider
    • cache_item_pool # 如果注入psr-6的实现体,则在使用Jwk请求时可以使用缓存功能。
# example: <project_root>/config/packages/o_auth2_service_provider.yml

o_auth2_service_provider:
  client_id: '%env(CLIENT_ID)%'
  client_secret: '%env(CLIENT_SECRET)%'
  authorize_url: https://account.dev.ridi.io/ridi/authorize/
  token_url: https://account.dev.ridi.io/oauth2/token/
  jwk_url: https://account.dev.ridi.io/oauth2/keys/public
  user_info_url: https://account.dev.ridi.io/accounts/me/
  token_cookie_domain: .ridi.io
  
  default_exception_handler: Ridibooks\OAuth2\Example\DefaultExceptionHandler
  cache_item_pool: Symfony\Component\Cache\Adapter\FilesystemAdapter
# example: <project_root>/config/services.yml

services:
  Ridibooks\OAuth2\Example\ExampleController:
    class: Ridibooks\OAuth2\Example\ExampleController
    autowire: true
    autoconfigure: true
    public: false
    arguments:
      - '@oauth2_service_provider'
3. 控制器设置
namespace Ridibooks\OAuth2\Example;

use Ridibooks\OAuth2\Symfony\Annotation\OAuth2;
use Ridibooks\OAuth2\Symfony\Provider\OAuth2ServiceProvider;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;

class ExampleController extends Controller
{
    /** @var OAuth2ServiceProvider */
    private $oauth2_service_provider;

    /**
     * @param OAuth2ServiceProvider $oauth2_service_provider
     */
    public function __construct(OAuth2ServiceProvider $oauth2_service_provider)
    {
        $this->oauth2_service_provider = $oauth2_service_provider;
    }

    /**
     * @Route("/oauth2", methods={"GET"})
     * @OAuth2()
     *
     * @param Request $request
     * @return Response
     */
    public function normal(Request $request): Response
    {
        $user = $this->oauth2_service_provider->getMiddleware()->getUser();

        return new JsonResponse([
            'u_idx' => $user->getUidx(),
            'u_id' => $user->getUid()
        ]);
    }
}

OAuth2异常处理器设置

  • 异常处理器负责处理OAuth2过程中发生错误时的异常情况。
  • 如果想要在Application Controller中使用除默认的default_exception_handler参数外的其他Exception Handler,请遵循以下步骤:
    • 创建一个实现了Ridibooks\OAuth2\Symfony\Handler\OAuth2ExceptionHandlerInterface的Exception Handler。
      • 示例:Ridibooks\Test\OAuth2\Symfony\TestExceptionHandler
    • 在Application Controller的@OAuth2注解中指定exception_handler属性。
      • 示例:@OAuth2(exception_handler="Ridibooks\Test\OAuth2\Symfony\TestExceptionHandler")

自定义用户提供者设置

  • 用户提供者在认证之后负责获取用户信息。
  • 如果没有指定 default_user_provider 参数,则默认使用 Ridibooks\OAuth2\Symfony\Provider\DefaultUserProvider
  • 如果想要在 Application Controller 中使用指定为 default_user_provider 的 User Provider 而不是其他 User Provider,请按照以下步骤操作。
    • 创建实现了 Ridibooks\OAuth2\Symfony\Provider\UserProviderInterface 的 User Provider。
    • 在 Application Controller 的 @OAuth2 注解中指定 user_provider 属性。
namespace Ridibooks\OAuth2\Example;

use Ridibooks\OAuth2\Authorization\Token\JwtToken;
use Ridibooks\OAuth2\Symfony\Provider\OAuth2ServiceProvider;
use Ridibooks\OAuth2\Symfony\Provider\UserProviderInterface;
use Symfony\Component\HttpFoundation\Request;

class CustomUserProvider implement UserProviderInterface
{
    /**
     * @param JwtToken $token
     * @param Request $request
     * @param OAuth2ServiceProvider $oauth2_service_provider
     * @return User
     */
    public function getUser(JwtToken $token, Request $request, OAuth2ServiceProvider $oauth2_service_provider): User
    {
        ...
    }
}
namespace Ridibooks\OAuth2\Example;

use Ridibooks\OAuth2\Symfony\Annotation\OAuth2;
use Ridibooks\OAuth2\Symfony\Provider\OAuth2ServiceProvider;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;

class ExampleController extends Controller
{
    /**
     * @Route("/oauth2", methods={"GET"})
     * @OAuth2(user_provider="Ridibooks\OAuth2\Example\CustomUserProvider")
     *
     * @param Request $request
     * @return Response
     */
    public function normal(Request $request): Response
    {
        ...
    }
}

Cache Item Pool 设置

  • Cache Item Pool 负责缓存 Jwk。

注意事项

  • 不支持 Jwk Multi signatures。只获取第一个索引的签名并对其进行解码。
  • Jwk Cache 文件的 TTL(Time To Live)是 5 分钟。