zfcampus / zf-mvc-auth
Requires
- php: ^5.6 || ^7.0
- zendframework/zend-authentication: ^2.5.3
- zendframework/zend-eventmanager: ^2.6.3 || ^3.0.1
- zendframework/zend-http: ^2.5.4
- zendframework/zend-mvc: ^2.7.9 || ^3.0.2
- zendframework/zend-permissions-acl: ^2.6
- zendframework/zend-permissions-rbac: ^2.5.1 || ^3.0
- zendframework/zend-servicemanager: ^2.7.6 || ^3.1
- zendframework/zend-stdlib: ^2.7.7 || ^3.0.1
- zfcampus/zf-api-problem: ^1.2.1
- zfcampus/zf-content-negotiation: ^1.2.1
- zfcampus/zf-oauth2: ^1.4
Requires (Dev)
- phpunit/phpunit: ^5.7.27 || ^6.5.8 || ^7.1.5
- zendframework/zend-coding-standard: ~1.0.0
- zendframework/zend-session: ^2.8.5
- dev-master / 1.5.x-dev
- dev-develop / 1.6.x-dev
- 1.5.1
- 1.5.0
- 1.4.3
- 1.4.2
- 1.4.1
- 1.4.0
- 1.3.2
- 1.3.1
- 1.3.0
- 1.2.1
- 1.2.0
- 1.1.3
- 1.1.2
- 1.1.1
- 1.1.0
- 1.0.3
- 1.0.2
- 1.0.1
- 1.0.0
- 1.0.0-beta3
- 1.0.0beta2
- 1.0.0beta1
- 0.9.1
- 0.9.0
- 0.8.0
- 0.7.0
- dev-feature/readme-documentation
- dev-hotfix/verbiage
- dev-feature/acl-authorization
- dev-feature/digest-auth
This package is auto-updated.
Last update: 2020-01-20 17:28:17 UTC
README
仓库废弃于2019-12-31
简介
zf-mvc-auth
是一个 ZF2 模块,它通过添加服务、事件和配置来扩展 ZF2 MVC 生命周期,以处理身份验证和授权。
对于身份验证,默认支持三种主要方法:HTTP Basic 身份验证、HTTP Digest 身份验证和 OAuth2(这需要 Brent Shaffer 的 OAuth2 Server)。
对于授权,该模块提供了一个预调度时间监听器,该监听器将确定给定的路由是否匹配,以及 HTTP 方法是否被授权进行调度。
要求
请参阅composer.json文件。
安装
运行以下composer
命令
$ composer require "zfcampus/zf-mvc-auth"
或者,手动将以下内容添加到您的composer.json
文件中的require
部分
"require": { "zfcampus/zf-mvc-auth": "^1.4" }
然后运行composer update
以确保安装了该模块。
最后,将模块名称添加到您的项目的config/application.config.php
文件中的modules
键下
return [ /* ... */ 'modules' => [ /* ... */ 'ZF\MvcAuth', ], /* ... */ ];
配置
用户配置
此模块用户配置的最高级别配置键是zf-mvc-auth
。在此键下有两个子键,一个是authentication
,另一个是authorization
。
键:authentication
authentication
键用于与身份验证过程或验证身份的过程相关的任何配置。
子键:http
http
子键用于配置基于 HTTP 的身份验证方案。这些方案使用 ZF2 的Zend\Authentication\Adapter\Http
适配器,该适配器实现了 HTTP Basic 和 HTTP Digest 身份验证。为此,HTTP 适配器使用基于文件的“解析器”来解析包含凭证的文件。这些实现细节可以在ZF2 手册中的身份验证部分中找到。
http
子键有几个字段
accept_schemes
: 必需;配置的方案数组;basic
和digest
之一或两个。realm
: 必需;这通常是一个标识 HTTP 域的字符串;例如,“我的网站”。digest_domains
: 必需(对于 HTTP Digest);这是受保护区域的相对 URI,通常是/
。nonce_timeout
: 对于HTTP摘要,是必需的;摘要nonce失效的秒数,通常为3600
。
除了这些配置选项之外,以下配置中的一项或两项是必需的
htpasswd
:指向一个以htpasswd
格式创建的文件的路径htdigest
:指向一个以htdigest
格式创建的文件的路径
示例可能如下所示
'http' => [ 'accept_schemes' => ['basic', 'digest'], 'realm' => 'My Web Site', 'digest_domains' => '/', 'nonce_timeout' => 3600, 'htpasswd' => APPLICATION_PATH . '/data/htpasswd', // htpasswd tool generated 'htdigest' => APPLICATION_PATH . '/data/htdigest', // @see http://www.askapache.com/online-tools/htpasswd-generator/ ],
子键:map
- 自1.1.0版本起。
map
子键用于将API模块(可选地,带有版本命名空间)映射到指定的认证类型(通常是basic
、digest
或oauth2
之一)。这可以用于对不同API或同一API的不同版本实施不同的认证方法。
return [ 'zf-mvc-auth' => [ 'authentication' => [ 'map' => [ 'Status\V1' => 'basic', // v1 only! 'Status\V2' => 'oauth2', // v2 only! 'Ping' => 'digest', // all versions! ], ], ], ];
如果没有map
子键,如果定义了任何认证适配器配置,则该配置将用于任何API。
注意:从1.0迁移的用户:在1.0系列中,认证是按应用程序进行的,而不是按API。迁移到1.1应该是无缝的;如果您未编辑您的认证设置,或向任何API提供认证信息,则您的API将继续按以前的方式运行。您第一次执行这些操作之一时,Admin API将创建一个映射,将每个服务的每个版本映射到配置的认证方案,从而确保您的API继续按先前配置的方式运行,同时让您能够将来按API和版本定义认证。
子键:types
- 自1.1.0版本起。
从1.1.0版本开始,提供了认证适配器的概念。适配器“提供”一个或多个认证类型;然后这些类型被用于内部确定使用哪个适配器,以及Admin API将API映射到特定的认证类型。
在某些情况下,您可能正在使用监听器或其他用于认证API的设施。为了允许将这些映射(在这种情况下主要是文档功能),存在types
子键。此键是字符串认证类型的数组
return [ 'zf-mvc-auth' => [ 'authentication' => [ 'types' => [ 'token', 'key', ], ], ], ];
此键及其内容必须手动创建。
子键:adapters
- 自1.1.0版本起。
从1.1.0版本开始,随着适配器的引入,您还可以配置命名的HTTP和OAuth2适配器。提供的名称将被用作映射API到认证适配器的认证类型。
adapters
键的格式是键/值对,其中键用作类型,值用作提供ZF\MvcAuth\Authentication\HttpAdapter
或ZF\MvcAuth\Authentication\OAuth2Adapter
实例的配置,如下所示
return [ 'zf-mvc-auth' => [ 'authentication' => [ 'adapters' => [ 'api' => [ // This defines an HTTP adapter that can satisfy both // basic and digest. 'adapter' => 'ZF\MvcAuth\Authentication\HttpAdapter', 'options' => [ 'accept_schemes' => ['basic', 'digest'], 'realm' => 'api', 'digest_domains' => 'https://example.com', 'nonce_timeout' => 3600, 'htpasswd' => 'data/htpasswd', 'htdigest' => 'data/htdigest', ], ], 'user' => [ // This defines an OAuth2 adapter backed by PDO. 'adapter' => 'ZF\MvcAuth\Authentication\OAuth2Adapter', 'storage' => [ 'adapter' => 'pdo', 'dsn' => 'mysql:host=localhost;dbname=oauth2', 'username' => 'username', 'password' => 'password', 'options' => [ 1002 => 'SET NAMES utf8', // PDO::MYSQL_ATTR_INIT_COMMAND ], ], ], 'client' => [ // This defines an OAuth2 adapter backed by Mongo. 'adapter' => 'ZF\MvcAuth\Authentication\OAuth2Adapter', 'storage' => [ 'adapter' => 'mongo', 'locator_name' => 'SomeServiceName', // If provided, pulls the given service 'dsn' => 'mongodb://localhost', 'database' => 'oauth2', 'options' => [ 'username' => 'username', 'password' => 'password', 'connectTimeoutMS' => 500, ], ], ], ], ], ], ];
键:authorization
子键:deny_by_default
deny_by_default
切换了Zend\Permissions\Acl
实现的默认行为。默认值是false
,这意味着如果没有认证用户存在,并且没有权限规则适用于当前资源,则允许访问。将此设置更改为true
以默认要求认证身份。
示例
'deny_by_default' => false,
使用zf-oauth2的deny_by_default
当使用
deny_by_default => true
与zf-oauth2
一起使用时,您需要显式允许OAuth2控制器的POST,以便进行认证请求。以下是一个示例
'authorization' => [ 'deny_by_default' => true, 'ZF\\OAuth2\\Controller\\Auth' => [ 'actions' => [ 'token' => [ 'GET' => false, 'POST' => true, // <----- 'PATCH' => false, 'PUT' => false, 'DELETE' => false, ], ], ], ],
子键:控制器服务名称
在 authorization
键下是一个由 控制器服务名称 作为键的授权配置设置数组。这些数组的结构取决于您尝试授予或限制访问的控制器服务的类型。
对于典型的基于 ZF2 的动作控制器,此数组以 actions
作为键。在此键下,给定控制器服务的每个动作名称都与一个 权限数组 关联。
对于基于 zf-rest 的控制器,使用顶级键 collection
或 entity
。在这些键下将会有一个相关的 权限数组。
权限数组 由键为数组的 default
或 HTTP 方法组成。这些值将是布尔值,其中 true
表示 需要认证用户,而 false
表示 不需要认证用户。如果一个动作或 HTTP 方法没有被标识,将假定 default
值。如果没有默认值,将假定上面讨论的 deny_by_default
键的行为。
下面是一个示例
'authorization' => [ 'Controller\Service\Name' => [ 'actions' => [ 'action' => [ 'default' => boolean, 'GET' => boolean, 'POST' => boolean, // etc. ], ], 'collection' => [ 'default' => boolean, 'GET' => boolean, 'POST' => boolean, // etc. ], 'entity' => [ 'default' => boolean, 'GET' => boolean, 'POST' => boolean, // etc. ], ], ],
系统配置
以下配置在 config/module.config.php
中提供,以启用模块的功能
'service_manager' => [ 'aliases' => [ 'authentication' => 'ZF\MvcAuth\Authentication', 'authorization' => 'ZF\MvcAuth\Authorization\AuthorizationInterface', 'ZF\MvcAuth\Authorization\AuthorizationInterface' => 'ZF\MvcAuth\Authorization\AclAuthorization', ], 'factories' => [ 'ZF\MvcAuth\Authentication' => 'ZF\MvcAuth\Factory\AuthenticationServiceFactory', 'ZF\MvcAuth\ApacheResolver' => 'ZF\MvcAuth\Factory\ApacheResolverFactory', 'ZF\MvcAuth\FileResolver' => 'ZF\MvcAuth\Factory\FileResolverFactory', 'ZF\MvcAuth\Authentication\DefaultAuthenticationListener' => 'ZF\MvcAuth\Factory\DefaultAuthenticationListenerFactory', 'ZF\MvcAuth\Authentication\AuthHttpAdapter' => 'ZF\MvcAuth\Factory\DefaultAuthHttpAdapterFactory', 'ZF\MvcAuth\Authorization\AclAuthorization' => 'ZF\MvcAuth\Factory\AclAuthorizationFactory', 'ZF\MvcAuth\Authorization\DefaultAuthorizationListener' => 'ZF\MvcAuth\Factory\DefaultAuthorizationListenerFactory', 'ZF\MvcAuth\Authorization\DefaultResourceResolverListener' => 'ZF\MvcAuth\Factory\DefaultResourceResolverListenerFactory', ], 'invokables' => [ 'ZF\MvcAuth\Authentication\DefaultAuthenticationPostListener' => 'ZF\MvcAuth\Authentication\DefaultAuthenticationPostListener', 'ZF\MvcAuth\Authorization\DefaultAuthorizationPostListener' => 'ZF\MvcAuth\Authorization\DefaultAuthorizationPostListener', ], ],
这些服务将在事件和服务部分中描述。
ZF2 事件
事件
ZF\MvcAuth\MvcAuthEvent::EVENT_AUTHENTICATION (也称为 "authentication")
此事件在 MvcEvent::EVENT_ROUTE
的 500
优先级下触发。它通过 ZF\MvcAuth\MvcRouteListener
事件监听器聚合进行注册。
ZF\MvcAuth\MvcAuthEvent::EVENT_AUTHENTICATION_POST (也称为 "authentication.post")
此事件在 MvcEvent::EVENT_ROUTE
的 499
优先级下触发。它通过 ZF\MvcAuth\MvcRouteListener
事件监听器聚合进行注册。
ZF\MvcAuth\MvcAuthEvent::EVENT_AUTHORIZATION (也称为 "authorization")
此事件在 MvcEvent::EVENT_ROUTE
的 -600
优先级下触发。它通过 ZF\MvcAuth\MvcRouteListener
事件监听器聚合进行注册。
ZF\MvcAuth\MvcAuthEvent::EVENT_AUTHORIZATION_POST (也称为 "authorization.post")
此事件在 MvcEvent::EVENT_ROUTE
的 -601
优先级下触发。它通过 ZF\MvcAuth\MvcRouteListener
事件监听器聚合进行注册。
ZF\MvcAuth\MvcAuthEvent 对象
当任何认证或授权事件被触发时,MvcAuthEvent
对象提供上下文信息。它保持以下内容
- 身份:
setIdentity()
和getIdentity()
- 认证服务:
setAuthentication()
和getAuthentication()
- 授权服务:
setAuthorization()
和getAuthorization()
- 授权结果:
setIsAuthorized
和isAuthorized()
- 原始 MVC 事件:
getMvcEvent()
监听器
ZF\MvcAuth\Authentication\DefaultAuthenticationListener
该监听器附加到 MvcAuth::EVENT_AUTHENTICATION
事件。它主要负责执行任何身份验证,并确保已验证的身份在 MvcAuthEvent
和 MvcEvent
对象中持久化(后者在事件参数 ZF\MvcAuth\Identity
下)。
ZF\MvcAuth\Authentication\DefaultAuthenticationPostListener
该监听器附加到 MvcAuth::EVENT_AUTHENTICATION_POST
事件。它主要负责确定是否执行了不成功的身份验证,如果是这种情况,它将尝试在 MvcEvent
的响应对象上设置 401 未授权
状态。
ZF\MvcAuth\Authorization\DefaultAuthorizationListener
该监听器附加到 MvcAuth::EVENT_AUTHORIZATION
事件。它主要负责在配置的授权服务上执行 isAuthorized()
方法。
ZF\MvcAuth\Authorization\DefaultAuthorizationPostListener
该监听器附加到 MvcAuth::EVENT_AUTHORIZATION_POST
事件。它主要负责确定当前请求是否被授权。在当前请求未授权的情况下,它将尝试在 MvcEvent
的响应对象上设置 403 禁止访问
状态。
ZF\MvcAuth\Authorization\DefaultResourceResolverListener
该监听器附加到 MvcAuth::EVENT_AUTHENTICATION_POST
,优先级为 -1
。它主要负责在配合 zf-rest
模块使用时,为基于 zf-rest 的控制器创建和持久化一个特殊名称。
ZF2 服务
控制器插件
该模块公开了控制器插件 getIdentity()
,映射到 ZF\MvcAuth\Identity\IdentityPlugin
。该插件将返回在身份验证过程中发现的身份,并将其注入到 Zend\Mvc\MvcEvent
的 ZF\MvcAuth\Identity
参数中。如果 MvcEvent
中不存在身份,或存在的是非 ZF\MvcAuth\Identity\IdentityInterface
实例的身份,则将返回一个 ZF\MvcAuth\Identity\GuestIdentity
实例。
事件监听器服务
以下服务提供并作为事件监听器使用
ZF\MvcAuth\Authentication\DefaultAuthenticationListener
ZF\MvcAuth\Authentication\DefaultAuthenticationPostListener
ZF\MvcAuth\Authorization\DefaultAuthorizationListener
ZF\MvcAuth\Authorization\DefaultAuthorizationPostListener
ZF\MvcAuth\Authorization\DefaultResourceResolverListener
ZF\MvcAuth\Authentication (即 "身份验证")
这是 Zend\Authentication\AuthenticationService
的一个实例。
ZF\MvcAuth\Authentication\AuthHttpAdapter
这是 Zend\Authentication\Adapter\Http
的一个实例。
ZF\MvcAuth\Authorization\AclAuthorization (即 "授权",ZF\MvcAuth\Authorization\AuthorizationInterface
)
这是 ZF\MvcAuth\Authorization\AclAuthorization
的一个实例,它又扩展了 Zend\Permissions\Acl\Acl
。
ZF\MvcAuth\ApacheResolver
这是Zend\Authentication\Adapter\Http\ApacheResolver
的一个实例。您可以通过提供自定义工厂来覆盖ApacheResolver,使用您自己的解析器。
ZF\MvcAuth\FileResolver
这是Zend\Authentication\Adapter\Http\FileResolver
的一个实例。您可以通过提供自定义工厂来覆盖FileResolver,使用您自己的解析器。
认证适配器
- 自1.1.0版本起
认证适配器提供了将自定义认证功能添加到API的最直接方法。适配器实现了ZF\MvcAuth\Authentication\AdapterInterface
namespace ZF\MvcAuth\Authentication; use Zend\Http\Request; use Zend\Http\Response; use ZF\MvcAuth\Identity\IdentityInterface; use ZF\MvcAuth\MvcAuthEvent; interface AdapterInterface { /** * @return array Array of types this adapter can handle. */ public function provides(); /** * Attempt to match a requested authentication type * against what the adapter provides. * * @param string $type * @return bool */ public function matches($type); /** * Attempt to retrieve the authentication type based on the request. * * Allows an adapter to have custom logic for detecting if a request * might be providing credentials it's interested in. * * @param Request $request * @return false|string */ public function getTypeFromRequest(Request $request); /** * Perform pre-flight authentication operations. * * Use case would be for providing authentication challenge headers. * * @param Request $request * @param Response $response * @return void|Response */ public function preAuth(Request $request, Response $response); /** * Attempt to authenticate the current request. * * @param Request $request * @param Response $response * @param MvcAuthEvent $mvcAuthEvent * @return false|IdentityInterface False on failure, IdentityInterface * otherwise */ public function authenticate(Request $request, Response $response, MvcAuthEvent $mvcAuthEvent); }
方法provides()
应返回一个字符串数组,每个字符串都是一个此适配器提供的认证“类型”;例如,提供的ZF\MvcAuth\Authentication\HttpAdapter
可以提供basic
和/或digest
。
matches($type)
方法应将给定的$type
与适配器提供的类型进行比较,以确定是否可以处理认证请求。通常,这可以通过return in_array($type, $this->provides(), true);
来完成
方法getTypeFromRequest()
可以用来将传入的请求与它解析的认证类型(如果有)相匹配。示例可能包括解析Authorization
头,或自定义头,如X-Api-Token
。
方法preAuth()
可以用来提供客户端挑战;通常,这只会由包含的HttpAdapter
使用。
最后,方法authenticate()
用于尝试认证传入的请求。它应返回一个布尔值false
,表示认证失败,或返回一个ZF\MvcAuth\Identity\IdentityInterface
的实例;如果返回后者的实例,则该身份将在请求期间使用。
适配器附加到DefaultAuthenticationListener
。要附加您自定义的适配器,您需要执行以下操作之一
- 通过配置定义命名HTTP和/或OAuth2适配器。
- 在事件监听器期间,获取您的适配器和
DefaultAuthenticationListener
服务,并将您的适配器附加到后者。 - 为
DefaultAuthenticationListener
创建一个DelegatorFactory
,在返回监听器之前附加您的自定义适配器。
定义命名的HTTP和/或OAuth2适配器
由于HTTP和OAuth2支持是内置的,因此zf-mvc-auth
提供了一种配置驱动的创建这些类型命名适配器的方法。每个类型都需要在zf-mvc-auth.authentication.adapters
配置下的唯一键,并且每种类型都有其自己的格式。
return [ /* ... */ 'zf-mvc-auth' => [ 'authentication' => [ 'adapters' => [ 'api' => [ // This defines an HTTP adapter that can satisfy both // basic and digest. 'adapter' => 'ZF\MvcAuth\Authentication\HttpAdapter', 'options' => [ 'accept_schemes' => ['basic', 'digest'], 'realm' => 'api', 'digest_domains' => 'https://example.com', 'nonce_timeout' => 3600, 'htpasswd' => 'data/htpasswd', 'htdigest' => 'data/htdigest', ], ], 'user' => [ // This defines an OAuth2 adapter backed by PDO. 'adapter' => 'ZF\MvcAuth\Authentication\OAuth2Adapter', 'storage' => [ 'adapter' => 'pdo', 'dsn' => 'mysql:host=localhost;dbname=oauth2', 'username' => 'username', 'password' => 'password', 'options' => [ 1002 => 'SET NAMES utf8', // PDO::MYSQL_ATTR_INIT_COMMAND ], ], ], 'client' => [ // This defines an OAuth2 adapter backed by Mongo. 'adapter' => 'ZF\MvcAuth\Authentication\OAuth2Adapter', 'storage' => [ 'adapter' => 'mongo', 'locator_name' => 'SomeServiceName', // If provided, pulls the given service 'dsn' => 'mongodb://localhost', 'database' => 'oauth2', 'options' => [ 'username' => 'username', 'password' => 'password', 'connectTimeoutMS' => 500, ], ], ], ], /* ... */ ], /* ... */ ], /* ... */ ];
上面的配置将为您的应用程序提供认证类型['api-basic', 'api-digest', 'user', 'client']
,这些类型可以与认证类型映射中的每个类型相关联。
如果您使用zf-apigility-admin
的Admin API和/或Apigility UI配置认证适配器,上述配置将为您创建。
在事件监听器期间附加适配器
在这种情况下,最佳事件是“认证”事件。这样做时,您将希望将优先级设置为> 1,以确保它在DefaultAuthenticationListener
之前执行。
在以下示例中,我们假设您已定义了一个名为MyCustomAuthenticationAdapter
的服务,该服务返回一个实现AdapterInterface
的实例,并且该类是您的API的Module
类或应用程序中的模块。
class Module { public function onBootstrap($e) { $app = $e->getApplication(); $events = $app->getEventManager(); $services = $app->getServiceManager(); $events->attach( 'authentication', function ($e) use ($services) { $listener = $services->get('ZF\MvcAuth\Authentication\DefaultAuthenticationListener') $adapter = $services->get('MyCustomAuthenticationAdapter'); $listener->attach($adapter); }, 1000 ); } }
通过返回空值,DefaultAuthenticationListener
将继续执行,但现在也将具有新附加的适配器。
使用代理工厂
代理工厂是一种“装饰”由Zend Framework的ServiceManager
返回的实例的方法,以便提供预条件或修改通常返回的实例。在我们的情况下,我们希望在实例创建后但在返回之前附加一个适配器。
在以下示例中,我们假设您已定义一个名为MyCustomAuthenticationAdapter
的服务,该服务返回一个AdapterInterface
实现。以下是一个为DefaultAuthenticationListener
提供的代理工厂,它将注入我们的适配器。
use Zend\ServiceManager\DelegatorFactoryInterface; use Zend\ServiceManager\ServiceLocatorInterface; class CustomAuthenticationDelegatorFactory implements DelegatorFactoryInterface { public function createDelegatorWithName( ServiceLocatorInterface $services, $name, $requestedName, $callback ) { $listener = $callback(); $listener->attach($services->get('MyCustomAuthenticationAdapter'); return $listener; } }
然后我们需要告诉ServiceManager
关于代理工厂的信息;我们在模块的config/module.config.php
文件或config/autoload/
配置文件之一中这样做。
return [ /* ... */ 'service_manager' => [ /* ... */ 'delegators' => [ 'ZF\MvcAuth\Authentication\DefaultAuthenticationListener' => [ 'CustomAuthenticationDelegatorFactory', ], ], ], /* ... */ ];
一旦配置完成,我们的适配器将被附加到检索到的每个DefaultAuthenticationListener
实例上。