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

编辑该页面

警告:你浏览的文档欧宝体育电话欧宝娱乐app下载地址Symfony 5.2,不再维护。

这个页面的更新版本Symf欧宝娱乐app下载地址ony 6.2(当前的稳定版本)。

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

谨慎

创建一个自定义的身份验证系统是困难的,而且几乎肯定需要的。相反,看到自定义的身份验证系统保护(API象征性的例子)对于一个简单的方法来创建一个身份验证系统你会喜欢。做继续阅读,除非你想学的最低水平认证的细节。

欧宝娱乐app下载地址Symfony提供了支持共同的身份验证机制。然而,您的应用程序可能需要与一些专有的单点登录系统或集成遗留身份验证机制。在这些情况下,您可以创建一个自定义的身份验证提供者。本文讨论了核心类参与认证过程,以及如何实现一个定制的身份验证提供者。因为身份验证和授权是不同的概念,这个扩展将user-provider不可知论者,并将与您的应用程序的功能用户提供者,他们可能位于内存数据库,或其他任何你选择存储它们。

满足WSSE

本文演示了如何创建一个自定义身份验证提供者WSSE身份验证。WSSE提供了一些保障福利的安全协议:

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

WSSE为web服务的获得是非常有用的,他们可能是SOAP或REST。

有大量的文档欧宝体育电话WSSE,但是本文将重点不是在安全协议,而是一个自定义协议的方式可以添加到您的Symfony应用程序。欧宝娱乐app下载地址WSSE的基础是请求头检查加密证书,使用时间戳和验证现时标志请求的用户使用一个密码,验证消化。

请注意

WSSE关键还支持应用程序进行验证时,web服务是有用的,但超出了本文的范围。

令牌

在Symfony安全上下文令牌的作用很重要。欧宝娱乐app下载地址一个令牌代表了用户身份验证数据出现在请求。一旦身份验证请求,令牌保留用户的数据,整个安全上下文,并将这些数据。首先,您将创建你的令牌类。这将允许所有相关信息的传递你的身份验证提供者:

1 2 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名称空间应用程序\安全\身份验证\令牌;使用欧宝娱乐app下载地址\组件\安全\核心\身份验证\令牌\AbstractToken;WsseUserToken扩展AbstractToken{公共美元创建;公共美元消化;公共美元现时标志;公共函数__construct(数组美元角色= []){::__construct (美元角色);/ /如果用户角色,考虑进行身份验证美元- >setAuthenticated (count (美元角色)>0);}公共函数getCredentials():字符串{返回;}}

请注意

WsseUserToken类扩展了安全组件的AbstractToken类,它提供了基本的标记功能。实现TokenInterface在任何类作为一个令牌。

侦听器

接下来,您需要一个监听器监听防火墙。侦听器负责防火墙部署请求,调用身份验证提供者。侦听器是一个可调用的,所以你必须实现一个__invoke ()方法。一个安全侦听器应该处理RequestEvent事件,并设置令牌的身份验证令牌存储如果成功:

1 2 3 4 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 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
/ / src /安全/防火墙/ WsseListener.php名称空间应用程序\安全\防火墙;使用应用程序\安全\身份验证\令牌\WsseUserToken;使用欧宝娱乐app下载地址\组件\HttpFoundation\响应;使用欧宝娱乐app下载地址\组件\HttpKernel\事件\RequestEvent;使用欧宝娱乐app下载地址\组件\安全\核心\身份验证\AuthenticationManagerInterface;使用欧宝娱乐app下载地址\组件\安全\核心\身份验证\令牌\存储\TokenStorageInterface;使用欧宝娱乐app下载地址\组件\安全\核心\异常\AuthenticationException;WsseListener{受保护的美元tokenStorage;受保护的美元authenticationManager;公共函数__construct(TokenStorageInterface美元tokenStorage,AuthenticationManagerInterface美元authenticationManager){美元- >tokenStorage =美元tokenStorage;美元- >authenticationManager =美元authenticationManager;}公共函数__invokeRequestEvent (美元事件):无效{美元请求=美元事件- >getRequest ();美元wsseRegex=' / UsernameToken用户名= " (? P <用户名> [^ "]+)”,PasswordDigest = " (? P <消化> [^]+)”,现时标志="(?P[a-zA-Z0-9+\/]+={0,2})", Created="(?P[^"]+)"/'< /span>;如果(!美元请求- >- >有(“x-wsse”)| |1! = = preg_match (美元wsseRegex,美元请求- >- >get (“x-wsse”),美元匹配)){返回;}美元令牌=WsseUserToken ();美元令牌- >setUser (美元匹配(“用户名”]);美元令牌- >消化=美元匹配(“消化”];美元令牌- >现时标志=美元匹配(“强奸犯”];美元令牌- >创建了=美元匹配(“创建”];试一试{美元authToken=美元- >authenticationManager- >验证(美元令牌);美元- >tokenStorage- >setToken (美元authToken);返回;}(AuthenticationException美元失败的){/ /……你可能会记录一些东西/ /否认身份验证令牌。这将重定向到登录页面。/ /一定要清楚你的令牌,而不是其他身份验证的听众。/ / $牌= $ this - > tokenStorage - > getToken ();/ /如果($牌instanceof WsseUserToken & & $ this - > providerKey = = = $令牌- > getProviderKey ()) {/ / $ this - > tokenStorage - > setToken(空);/ /}/ /返回;}/ /默认拒绝授权美元响应=反应();美元响应- >setStatusCode(响应::HTTP_FORBIDDEN);美元事件- >setResponse (美元响应);}}

该侦听器检查请求的预期X-WSSE头,匹配预期的返回值WSSE信息,使用这些信息创建一个令牌,令牌传递身份验证管理器。如果不提供适当的信息,或身份验证管理器抛出AuthenticationException,将返回一个401响应。

请注意

上面一个类不习惯,AbstractAuthenticationListener类,是一种非常有用的基类为安全提供通常需要的功能扩展。这包括维护会话令牌,提供成功/失败处理程序,登录表单url,以及更多。因为WSSE不需要维护或登录的身份验证会话形式,它不会被用于此示例。

请注意

过早返回于侦听器相关只有如果你想链身份验证提供者(例如,允许匿名用户)。如果你想禁止访问匿名用户和有一个404错误,你应该设定响应返回的状态码。

身份验证提供者

身份验证提供者的验证WsseUserToken。也就是说,供应商将验证创建头值是有效的在五分钟内,现时标志头的价值是独一无二的在五分钟内,PasswordDigest头值匹配用户的密码:

1 2 3 4 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 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;使用欧宝娱乐app下载地址\组件\安全\核心\身份验证\提供者\AuthenticationProviderInterface;使用欧宝娱乐app下载地址\组件\安全\核心\身份验证\令牌\TokenInterface;使用欧宝娱乐app下载地址\组件\安全\核心\异常\AuthenticationException;使用欧宝娱乐app下载地址\组件\安全\核心\用户\UserProviderInterface;WsseProvider实现了AuthenticationProviderInterface{私人美元userProvider;私人美元cachePool;公共函数__construct(UserProviderInterface美元userProvider,CacheItemPoolInterface美元cachePool){美元- >userProvider =美元userProvider;美元- >cachePool =美元cachePool;}公共函数进行身份验证(TokenInterface美元令牌):WsseUserToken{美元用户=美元- >userProvider- >loadUserByUsername (美元令牌- >getUsername ());如果(美元用户& &美元- >validateDigest (美元令牌- >消化,美元令牌- >现时标志,美元令牌- >创建,美元用户- >getPassword ())) {美元authenticatedToken=WsseUserToken (美元用户- >将getRoles ());美元authenticatedToken- >setUser (美元用户);返回美元authenticatedToken;}AuthenticationException (“WSSE身份验证失败了。”);}/ * * *这个函数是特定于Wsse身份验证和这个例子只是用来帮助* *特定逻辑的更多信息,参见https://github.com/symfony/symfony-docs/pull/3134 * # issuecomment - 27699129 * /欧宝娱乐app下载地址受保护的函数validateDigest(美元消化,美元现时标志,美元创建,美元秘密):bool{/ /检查创建时间不是在未来如果(strtotime (美元创建)>时间()){返回;}/ /时间戳5分钟后过期如果(时间()——strtotime (美元创建)>300年){返回;}/ /从池获取缓存项美元cacheItem=美元- >cachePool- >getItem (md5 (美元现时标志));/ /验证nonce *不*在缓存中/ /如果是,这可能是一个重放攻击如果(美元cacheItem- >isHit ()) {/ /在现实世界的应用程序你应该抛出一个定制的/ /扩展AuthenticationException异常AuthenticationException (“以前nonce检测”);}/ /存储在缓存项5分钟美元cacheItem- >集()- >expiresAfter (300年);美元- >cachePool- >保存(美元cacheItem);/ /验证的秘密美元预期= base64_encode (sha1 (base64_decode (美元现时标志)。美元创建美元秘密,真正的));返回hash_equals (美元预期,美元消化);}公共函数支持(TokenInterface美元令牌):bool{返回美元令牌运算符WsseUserToken;}}

请注意

AuthenticationProviderInterface需要一个authenticate ()方法在用户令牌和一个支持()方法,它告诉身份验证管理器使用这个供应商是否为给定的令牌。在多个提供者的情况下,认证管理器将会移动到下一个提供者列表中。

工厂

您已经创建了一个自定义令牌、自定义侦听器和自定义服务提供方程序。现在你需要把它们放在一起。你如何为每个防火墙提供一个独特的供应商吗?答案是通过使用一个工厂。工厂是你钩到安全组件,告诉您的供应商的名称和任何配置选项。首先,您必须创建一个类实现SecurityFactoryInterface:

1 2 3 4 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;使用欧宝娱乐app下载地址\\SecurityBundle\DependencyInjection\安全\工厂\SecurityFactoryInterface;使用欧宝娱乐app下载地址\组件\配置\定义\构建器\NodeDefinition;使用欧宝娱乐app下载地址\组件\DependencyInjection\ChildDefinition;使用欧宝娱乐app下载地址\组件\DependencyInjection\ContainerBuilder;使用欧宝娱乐app下载地址\组件\DependencyInjection\参考;WsseFactory实现了SecurityFactoryInterface{公共函数创建(ContainerBuilder美元容器、字符串美元id数组,美元配置、字符串美元userProvider字符串,?美元defaultEntryPoint):数组{美元providerId=“security.authentication.provider.wsse”。美元id;美元容器- >setDefinition (美元providerId,ChildDefinition (WsseProvider::类))- >setArgument (0,引用(美元userProvider));美元listenerId=“security.authentication.listener.wsse”。美元id;美元容器- >setDefinition (美元listenerId,ChildDefinition (WsseListener::类));返回(美元providerId,美元listenerId,美元defaultEntryPoint];}公共函数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
1 2 3 4 5 6 7 8 9 10
#配置/ services.yaml服务:#……App \安全\身份验证提供者\ \ WsseProvider:参数:$ cachePool:“@cache.app”应用防火墙\安全\ \ WsseListener:参数:(“@security.token_storage”,“@security.authentication.manager”]

现在你的服务定义,告诉你关于贵厂在内核安全上下文:

1 2 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 ()方法:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/ / src / DependencyInjection /安全/工厂/ WsseFactory.php名称空间应用程序\DependencyInjection\安全\工厂;/ /……WsseFactory实现了SecurityFactoryInterface{/ /……公共函数addConfiguration(NodeDefinition美元节点):无效{美元节点- >孩子()- >scalarNode (“一生”)- >defaultValue (300年)- >结束();}}

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

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/ / src / DependencyInjection /安全/工厂/ WsseFactory.php名称空间应用程序\DependencyInjection\安全\工厂;使用应用程序\安全\身份验证\提供者\WsseProvider;WsseFactory实现了SecurityFactoryInterface{公共函数创建(ContainerBuilder美元容器、字符串美元id数组,美元配置、字符串美元userProvider字符串,?美元defaultEntryPoint):数组{美元providerId=“security.authentication.provider.wsse”。美元id;美元容器- >setDefinition (美元providerId,ChildDefinition (WsseProvider::类))- >setArgument (0,引用(美元userProvider))- >setArgument (2,美元配置(“一生”]);/ /……}/ /……}

请注意

WsseProvider类将现在也需要接受第三个构造函数参数——一生——它应该使用而不是硬编码的300秒。这一步是这里没有显示。

的生命周期每个WSSE请求现在是可配置的,并且可以设置为任何可取的价值/防火墙。

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

剩下的是你!任何相关的配置项可以定义在工厂和消费或传递到其他类的容器。

这项工作,包括代码示例,许可下Creative Commons冲锋队3.0许可证。