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

编辑本页

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

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

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

提示

创建自定义身份验证系统非常困难,本文将引导您完成该过程。但根据你的需要,你可以用更简单的方式解决你的问题,或者通过社区包:欧宝体育平台怎么样

如果你读过这篇文章安全,您应该了解Symfony在安全性实现中的身份验证和授权之间的区别ob娱乐下载。本文讨论身份验证过程中涉及的核心类,以及如何实现自定义身份验证提供程序。因为身份验证和授权是分开的概念,所以这个扩展将与用户提供程序无关,并且将与应用程序的用户提供程序一起工作,可能它们基于内存、数据库或您选择存储它们的其他任何地方。

满足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 / AppBundle /安全/认证/令牌/ WsseUserToken.php名称空间AppBundle安全身份验证令牌使用ob娱乐下载组件安全核心身份验证令牌AbstractTokenWsseUserToken扩展AbstractToken公共创建公共消化公共现时标志公共函数__construct(数组角色=数组()::__construct (角色);//如果用户有角色,则认为认证成功->setAuthenticated (count (角色) >0);}公共函数getCredentials()返回;}}

请注意

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

侦听器

接下来,需要一个侦听器在防火墙上侦听。监听器负责向防火墙发送请求并调用身份验证提供程序。监听器必须是的实例ListenerInterface.安全侦听器应该处理GetResponseEvent事件,如果成功,在令牌存储中设置一个认证的令牌:

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 61
/ / src / AppBundle /安全/防火墙/ WsseListener.php名称空间AppBundle安全防火墙使用ob娱乐下载组件HttpFoundation响应使用ob娱乐下载组件HttpKernel事件GetResponseEvent使用ob娱乐下载组件安全核心身份验证AuthenticationManagerInterface使用ob娱乐下载组件安全核心身份验证令牌存储TokenStorageInterface使用ob娱乐下载组件安全核心异常AuthenticationException使用ob娱乐下载组件安全Http防火墙ListenerInterface使用AppBundle安全身份验证令牌WsseUserTokenWsseListener实现了ListenerInterface受保护的tokenStorage受保护的authenticationManager公共函数__construct(TokenStorageInterfacetokenStorage, AuthenticationManagerInterfaceauthenticationManager->tokenStorage =tokenStorage->authenticationManager =authenticationManager;}公共函数处理(GetResponseEvent事件请求事件->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
/ / src / AppBundle /安全/认证/供应商/ WsseProvider.php名称空间AppBundle安全身份验证提供者使用ob娱乐下载组件安全核心身份验证提供者AuthenticationProviderInterface使用ob娱乐下载组件安全核心用户UserProviderInterface使用ob娱乐下载组件安全核心异常AuthenticationException使用ob娱乐下载组件安全核心异常NonceExpiredException使用ob娱乐下载组件安全核心身份验证令牌TokenInterface使用AppBundle安全身份验证令牌WsseUserTokenWsseProvider实现了AuthenticationProviderInterface私人userProvider私人cacheDirectory公共函数__construct(UserProviderInterfaceuserProvidercacheDirectory->userProvider =userProvider->cacheDirectory =cacheDirectory;}公共函数进行身份验证(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){返回;}//验证nonce在最近5分钟内没有被使用//如果有,这可能是一个重放攻击如果(file_exists (->cacheDirectory。' / '.md5 (现时标志file_get_contents())->cacheDirectory。' / '.md5 (现时标志) +300> time()) {NonceExpiredException (“以前使用过,立即检测到”);}//如果缓存目录不存在,我们创建它如果(!is_dir (->cacheDirectory) {mkdir(->cacheDirectory,0777真正的);}写入->cacheDirectory。' / '.md5 (现时标志)、时间());//验证秘密预期= base64_encode (sha1 (base64_decode (现时标志).创建秘密真正的));返回hash_equals (预期消化);}公共函数支持(TokenInterface令牌返回令牌运算符WsseUserToken;}}

请注意

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

请注意

hash_equals函数是在PHP 5.6中引入的,您可以安全地在Symfony应用程序的任何PHP版本中使用它。ob娱乐下载在5.6之前的PHP版本中,ob娱乐下载Symfony Polyfill(包含在Symfony中)将为您定义函数。ob娱乐下载

2.8

ob娱乐下载Symfony Polyfill自Symfony 2.8开始默认包含。在Symfony ob娱乐下载2.8之前,您必须执行需要symfony/polyfilob娱乐下载l-php56能够使用hash_equals旧的PHP版本。

工厂

您已经创建了自定义令牌、自定义侦听器和自定义提供程序。现在你需要把它们都绑在一起。如何为每个防火墙提供唯一的提供程序?答案是使用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
/ / src / AppBundle / DependencyInjection /安全/工厂/ WsseFactory.php名称空间AppBundleDependencyInjection安全工厂使用ob娱乐下载组件DependencyInjectionContainerBuilder使用ob娱乐下载组件DependencyInjection参考使用ob娱乐下载组件DependencyInjectionDefinitionDecorator使用ob娱乐下载组件配置定义构建器NodeDefinition使用ob娱乐下载SecurityBundleDependencyInjection安全工厂SecurityFactoryInterfaceWsseFactory实现了SecurityFactoryInterface公共函数创建(ContainerBuilder容器id配置userProviderdefaultEntryPointproviderId“security.authentication.provider.wsse”。id容器->setDefinition (providerIdDefinitionDecorator (“wsse.security.authentication.provider”))->replaceArgument (0引用(userProvider));listenerId“security.authentication.listener.wsse”。id容器->setDefinition (listenerIdDefinitionDecorator (“wsse.security.authentication.listener”));返回数组providerIdlistenerIddefaultEntryPoint);}公共函数getPosition()返回“pre_auth”;}公共函数getKey()返回“wsse”;}公共函数addConfiguration(NodeDefinition节点{}}

SecurityFactoryInterface需要以下方法:

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

请注意

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

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

请注意

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

配置

现在是时候看看您的身份验证提供程序的运行情况了。你需要做几件事来实现这个目标。第一件事是将上述服务添加到DI容器中。上面的工厂类引用了还不存在的服务id:wsse.security.authentication.provider而且wsse.security.authentication.listener.是时候定义这些服务了。

  • YAML
  • XML
  • PHP
12 3 4 5 6 7 8 9 10 11 12 13
# app / config / services.yml服务:wsse.security.authentication.provider:类:AppBundle \安全\验证\ \ WsseProvider提供者参数:-#用户提供商-“% kernel.cache_dir % /安全/目前”公众:wsse.security.authentication.listener:类:AppBundle \安全\ \ WsseListener防火墙参数:(“@security.token_storage”,“@security.authentication.manager”公众:

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

12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/ / src / AppBundle / AppBundle.php名称空间AppBundle使用AppBundleDependencyInjection安全工厂WsseFactory使用ob娱乐下载组件HttpKernel使用ob娱乐下载组件DependencyInjectionContainerBuilderAppBundle扩展公共函数构建(ContainerBuilder容器::构建(容器);扩展容器->getExtension (“安全”);扩展->addSecurityListenerFactory (WsseFactory ());}}

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

  • YAML
  • XML
  • PHP
1 2 3 4 5 6 7 8 9
# app / config / security.yml安全:#……防火墙: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
WsseFactory实现了SecurityFactoryInterface公共函数创建(ContainerBuilder容器id配置userProviderdefaultEntryPointproviderId“security.authentication.provider.wsse”。id容器->setDefinition (providerIdDefinitionDecorator (“wsse.security.authentication.provider”))->replaceArgument (0引用(userProvider))->replaceArgument (2配置“一生”]);/ /……/ /……

请注意

的参数中还需要添加第三个参数wsse.security.authentication.provider服务配置,它可以是空白的,但将在工厂的生命周期中填写。的WsseProvider类现在还需要接受第三个构造函数参数——lifetime——它应该使用这个参数,而不是硬编码的300秒。这里没有显示这两个步骤。

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

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

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

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