如何创建自定义身份验证提供者

编辑本页

警告:您正在浏览的文档欧宝官网下载appob娱乐下载Symfony 4.3,现已不再维护。

本页的更新版本用于Syob娱乐下载mfony 6.2(当前稳定版本)。

如何创建自定义身份验证提供者

谨慎

创建一个自定义身份验证系统是困难的,而且几乎是肯定的需要的。相反,看到带Guard的自定义认证系统(API令牌示例)一个简单的方法来创建一个认证系统,你会喜欢。做请继续阅读,除非您想了解身份验证的最低级别细节。

ob娱乐下载Symfony提供的支持最多通用身份验证机制.但是,您的应用程序可能需要与一些专有的单点登录系统或一些遗留的身份验证机制集成。在这些情况下,您可以创建一个自定义身份验证提供程序。本文讨论身份验证过程中涉及的核心类,以及如何实现自定义身份验证提供程序。因为身份验证和授权是分开的概念,所以这个扩展将与用户提供程序无关,并且将与应用程序的用户提供程序一起工作,可能它们基于内存、数据库或您选择存储它们的其他任何地方。

满足WSSE

下面的文章演示如何为WSSE身份验证创建自定义身份验证提供程序。WSSE的安全协议提供了几个安全优势:

  1. 用户名/密码加密
  2. 安全防范重放攻击
  3. 不需要web服务器配置

WSSE对于web服务的安全非常有用,不管它们是SOAP还是REST。

有很多很棒的文档欧宝官网下载appWSSE,但本文的重点不是安全协议,而是将自定义协议添加到Symfony应用程序的方式。ob娱乐下载WSSE的基础是检查请求头是否加密凭据,使用时间戳和验证现时标志,并使用密码摘要为所请求的用户进行身份验证。

请注意

WSSE还支持应用程序密钥验证,这对web服务很有用,但超出了本文的范围。

令牌

令牌在Symfony安全上下文中的作用非常重要。ob娱乐下载令牌表示请求中出现的用户身份验证数据。请求通过身份验证后,令牌将保留用户的数据,并在安全上下文中传递该数据。首先,您将创建令牌类。这将允许将所有相关信息传递给您的身份验证提供者:

12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
/ / src /安全/认证/令牌/ WsseUserToken.php名称空间应用程序安全身份验证令牌使用ob娱乐下载组件安全核心身份验证令牌AbstractTokenWsseUserToken扩展AbstractToken{公共创建公共消化公共现时标志公共函数__construct(数组角色= []){::__construct (角色);//如果用户有角色,则认为认证成功->setAuthenticated (count (角色) >0);}公共函数getCredentials(){返回;}}

请注意

WsseUserToken类扩展了Security组件的AbstractToken类,它提供基本令牌功能。实现TokenInterface在任何类上用作令牌。

侦听器

接下来,需要一个侦听器在防火墙上侦听。监听器负责向防火墙发送请求并调用身份验证提供程序。Listener是一个可调用对象,因此必须实现一个__invoke ()方法。安全侦听器应该处理RequestEvent事件,如果成功,在令牌存储中设置一个认证的令牌:

12 34 56 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
/ / src /安全/防火墙/ WsseListener.php名称空间应用程序安全防火墙使用应用程序安全身份验证令牌WsseUserToken使用ob娱乐下载组件HttpFoundation响应使用ob娱乐下载组件HttpKernel事件RequestEvent使用ob娱乐下载组件安全核心身份验证AuthenticationManagerInterface使用ob娱乐下载组件安全核心身份验证令牌存储TokenStorageInterface使用ob娱乐下载组件安全核心异常AuthenticationExceptionWsseListener{受保护的tokenStorage受保护的authenticationManager公共函数__construct(TokenStorageInterfacetokenStorage, AuthenticationManagerInterfaceauthenticationManager{->tokenStorage =tokenStorage->authenticationManager =authenticationManager;}公共函数__invokeRequestEvent (事件{请求事件->getRequest ();wsseRegex' / UsernameToken用户名= " (? P <用户名 >[^"]+)", PasswordDigest = " (? P <消化 >[^"]+)", 现时标志= " (? P < Nonce > [a-zA-Z0-9 + \ /] + ={0, 2})”,创建= " (? P < >创建[^ "]+)“/”如果(!请求->->有(“x-wsse”) | |1! = = preg_match (wsseRegex请求->->get (“x-wsse”),匹配)) {返回;}令牌WsseUserToken ();令牌->setUser (匹配“用户名”]);令牌->消化=匹配“消化”];令牌->现时标志=匹配“强奸犯”];令牌->创建了=匹配“创建”];试一试{authToken->authenticationManager->验证(令牌);->tokenStorage->setToken (authToken);返回;}(AuthenticationException失败的) {/ /……你可以在这里记录一些东西//拒绝认证,清除令牌。这将重定向到登录页面。//确保只清除您的令牌,而不是其他身份验证侦听器的令牌。// $token = $this->tokenStorage->getToken();// if ($token instanceof WsseUserToken && $this->providerKey === $token->getProviderKey()) {/ / $ this - > tokenStorage - > setToken(空);/ /}/ /返回;//默认拒绝授权响应反应();响应->setStatusCode(响应::HTTP_FORBIDDEN);事件->setResponse (响应);}}

这个侦听器检查请求是否有预期的X-WSSE头,匹配预期WSSE信息的返回值,使用该信息创建一个令牌,并将令牌传递给身份验证管理器。如果没有提供正确的信息,或者身份验证管理器抛出AuthenticationException,返回403响应。

请注意

上面没有使用的类AbstractAuthenticationListener类,是一个非常有用的基类,它为安全扩展提供了常用的功能。这包括维护会话中的令牌、提供成功/失败处理程序、登录表单url等等。由于WSSE不需要维护身份验证会话或登录表单,因此本示例不使用它。

请注意

只有当您希望链接身份验证提供者(例如允许匿名用户)时,才需要从侦听器过早返回。如果您希望禁止匿名用户访问,并且有一个403错误,那么您应该在返回之前设置响应的状态代码。

身份验证提供者

身份验证提供程序将对WsseUserToken.也就是说,提供者将验证创建头值在五分钟内有效,则现时标志头值在五分钟内是唯一的,并且PasswordDigest头值与用户密码匹配:

12 34 56 78 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
/ / src /安全/认证/供应商/ WsseProvider.php名称空间应用程序安全身份验证提供者使用应用程序安全身份验证令牌WsseUserToken使用Psr缓存CacheItemPoolInterface使用ob娱乐下载组件安全核心身份验证提供者AuthenticationProviderInterface使用ob娱乐下载组件安全核心身份验证令牌TokenInterface使用ob娱乐下载组件安全核心异常AuthenticationException使用ob娱乐下载组件安全核心用户UserProviderInterfaceWsseProvider实现了AuthenticationProviderInterface{私人userProvider私人cachePool公共函数__construct(UserProviderInterfaceuserProvider, CacheItemPoolInterfacecachePool{->userProvider =userProvider->cachePool =cachePool;}公共函数进行身份验证(TokenInterface令牌{用户->userProvider->loadUserByUsername (令牌->getUsername ());如果用户& &->validateDigest (令牌->消化,令牌->现时标志,令牌->创建,用户->getPassword ())) {authenticatedTokenWsseUserToken (用户->将getRoles ());authenticatedToken->setUser (用户);返回authenticatedToken;}AuthenticationException (“WSSE认证失败。”);}/** *此函数特定于Wsse身份验证,仅用于帮助本示例**有关此处特定于逻辑的更多信息,请参见* https://github.com/symfony/symfony-docs/pull/3134#issuecomment-27699129 */ob娱乐下载受保护的函数validateDigest消化现时标志创建秘密{//检查创建的时间不在未来如果(strtotime (创建> time()) {返回;}// 5分钟后失效时间戳如果(time() - strtotime(创建) >300) {返回;}//尝试从池中获取缓存项cacheItem->cachePool->getItem (md5 (现时标志));//确认nonce不在缓存中//如果是,这可能是一个重放攻击如果cacheItem->isHit ()) {//在实际应用程序中,您应该抛出一个自定义// exception扩展AuthenticationExceptionAuthenticationException (“以前使用过,立即检测到”);}//在缓存中存储5分钟cacheItem->集(->expiresAfter (300);->cachePool->保存(cacheItem);//验证秘密预期= base64_encode (sha1 (base64_decode (现时标志).创建秘密真正的));返回hash_equals (预期消化);}公共函数支持(TokenInterface令牌{返回令牌运算符WsseUserToken;}}

请注意

AuthenticationProviderInterface需要一个authenticate ()方法在用户令牌上调用支持()方法,该方法告诉身份验证管理器是否对给定的令牌使用此提供程序。在有多个提供者的情况下,身份验证管理器将移动到列表中的下一个提供者。

工厂

您已经创建了自定义令牌、自定义侦听器和自定义提供程序。现在你需要把它们都绑在一起。如何为每个防火墙提供唯一的提供程序?答案是使用a工厂.工厂是您钩子到Security组件的地方,告诉它您的提供者的名称和它可用的任何配置选项。首先,必须创建一个实现SecurityFactoryInterface

12 34 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
/ / src / DependencyInjection /安全/工厂/ WsseFactory.php名称空间应用程序DependencyInjection安全工厂使用应用程序安全身份验证提供者WsseProvider使用应用程序安全防火墙WsseListener使用ob娱乐下载SecurityBundleDependencyInjection安全工厂SecurityFactoryInterface使用ob娱乐下载组件配置定义构建器NodeDefinition使用ob娱乐下载组件DependencyInjectionChildDefinition使用ob娱乐下载组件DependencyInjectionContainerBuilder使用ob娱乐下载组件DependencyInjection参考WsseFactory实现了SecurityFactoryInterface{公共函数创建(ContainerBuilder容器id配置userProviderdefaultEntryPoint{providerId“security.authentication.provider.wsse”。id容器->setDefinition (providerIdChildDefinition (WsseProvider::类))->setArgument (0引用(userProvider));listenerId“security.authentication.listener.wsse”。id容器->setDefinition (listenerIdChildDefinition (WsseListener::类));返回providerIdlistenerIddefaultEntryPoint];}公共函数getPosition(){返回“pre_auth”;}公共函数getKey(){返回“wsse”;}公共函数addConfiguration(NodeDefinition节点{}}

SecurityFactoryInterface需要以下方法:

create ()
方法,该方法将侦听器和身份验证提供程序添加到适当的安全上下文的DI容器中。
getPosition ()
在应该调用提供程序时返回。这可能是其中之一pre_auth形式httpremember_me
getKey ()
方法,该方法定义用于在防火墙配置中引用提供程序的配置键。
addConfiguration ()
方法,该方法用于定义安全配置中配置键下面的配置选项。本文后面将解释设置配置选项。

请注意

本例中未使用的类,AbstractFactory,是一个非常有用的基类,它为安全工厂提供了常用的功能。在定义不同类型的身份验证提供程序时,它可能很有用。

既然您已经创建了一个工厂类,那么wsse密钥可以在安全配置中用作防火墙。

请注意

您可能想知道“为什么需要一个特殊的工厂类来向依赖注入容器添加监听器和提供程序?”这是一个非常好的问题。原因是您可以多次使用防火墙,以保护应用程序的多个部分。因此,每次使用防火墙时,都会在DI容器中创建一个新服务。工厂创建了这些新服务。

配置

现在是时候看看您的身份验证提供程序的运行情况了。你需要做几件事来实现这个目标。第一件事是将上述服务添加到DI容器中。上面的工厂类引用了可能还不存在的服务id:应用程序\安全\身份验证\提供者\ WsseProvider而且应用防火墙\安全\ \ WsseListener.是时候定义这些服务了。

  • YAML
  • XML
  • PHP
12 3 4 5 6 7 8 9 10 11 12
#配置/ services.yaml服务:#……App \安全\身份验证提供者\ \ WsseProvider:参数:$ cachePool:“@cache.app”公众:应用防火墙\安全\ \ WsseListener:参数:(“@security.token_storage”,“@security.authentication.manager”]公众:

现在你的服务已经定义好了,告诉你的安全上下文关于你在内核中的工厂:

12 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/ / src / Kernel.php名称空间应用程序使用应用程序DependencyInjection安全工厂WsseFactory/ /……内核扩展BaseKernel{公共函数构建(ContainerBuilder容器{扩展容器->getExtension (“安全”);扩展->addSecurityListenerFactory (WsseFactory ());}/ /……

你完蛋了!现在,您可以将应用程序的某些部分定义为受WSSE保护。

  • YAML
  • XML
  • PHP
1 2 3 4 5 6 7 8 9
#配置/包/ security.yaml安全:#……防火墙:wsse_secured:模式:^ / api /无状态:真正的wsse:真正的

恭喜你!您已经编写了自己的自定义安全身份验证提供程序!

额外的一点

让您的WSSE身份验证提供程序更令人兴奋一点怎么样?可能性是无限的。你为什么不先给你的光芒加点亮点呢?

配置

属性下添加自定义选项wsse输入安全配置。方法到期前允许的时间创建默认情况下,头项为5分钟。使其可配置,这样不同的防火墙可以有不同的超时长度。

你首先需要编辑WsseFactory属性中定义新选项addConfiguration ()方法:

12 3 4 5 6 7 8 9 10 11 12
WsseFactory实现了SecurityFactoryInterface{/ /……公共函数addConfiguration(NodeDefinition节点{节点->孩子()->scalarNode (“一生”->defaultValue (300->结束();}}

现在,在create ()方法的工厂,该美元配置参数将包含一生键,设置为5分钟(300秒),除非在配置中另有设置。将这个参数传递给你的身份验证提供者,以便使用它:

12 3 4 5 6 7 8 9 10 11 12 13 14 15 16
使用应用程序安全身份验证提供者WsseProviderWsseFactory实现了SecurityFactoryInterface{公共函数创建(ContainerBuilder容器id配置userProviderdefaultEntryPoint{providerId“security.authentication.provider.wsse”。id容器->setDefinition (providerIdChildDefinition (WsseProvider::类))->setArgument (0引用(userProvider))->setArgument (2配置“一生”]);/ /……/ /……

请注意

WsseProvider类现在还需要接受第三个构造函数参数——lifetime——它应该使用这个参数,而不是硬编码的300秒。此处未显示此步骤。

每个WSSE请求的生存期现在都是可配置的,并且可以为每个防火墙设置任何所需的值。

  • YAML
  • XML
  • PHP
1 2 3 4 5 6 7 8 9
#配置/包/ security.yaml安全:#……防火墙:wsse_secured:模式:^ / api /无状态:真正的wsse:{生命周期:30.

剩下的就看你了!任何相关的配置项都可以在工厂中定义,并使用或传递给容器中的其他类。

此工作,包括代码示例,是根据创作共用BY-SA 3.0许可证。