如何验证用户身份的API密钥

编辑该页面

警告:你浏览的文档欧宝官网下载appob娱乐下载Symfony 3.0,不再维护。

这个页面的更新版本Symfob娱乐下载ony 6.2(当前的稳定版本)。

如何验证用户身份的API密钥

提示

看看如何创建一个自定义的身份验证系统,保安吗一个更简单、更灵活的方式完成自定义的身份验证任务。

如今,很平常认证用户通过一个API密匙(例如在开发一个web服务)。为每个请求和提供的API密钥是作为查询字符串参数传递或通过一个HTTP头。

API密钥身份验证

验证用户基于请求信息应该通过pre-authentication机制。的SimplePreAuthenticatorInterface可以实现这样的计划真的很容易。

你的具体情况可能有所不同,但是在这个例子中,读取一个令牌apikey查询参数,从这个值加载适当的用户名,然后创建一个用户对象:

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
/ / src / AppBundle /安全/ ApiKeyAuthenticator.php名称空间AppBundle\安全;使用ob娱乐下载\组件\HttpFoundation\请求;使用ob娱乐下载\组件\安全\核心\身份验证\令牌\PreAuthenticatedToken;使用ob娱乐下载\组件\安全\核心\身份验证\令牌\TokenInterface;使用ob娱乐下载\组件\安全\核心\异常\AuthenticationException;使用ob娱乐下载\组件\安全\核心\异常\CustomUserMessageAuthenticationException;使用ob娱乐下载\组件\安全\核心\异常\BadCredentialsException;使用ob娱乐下载\组件\安全\核心\用户\UserProviderInterface;使用ob娱乐下载\组件\安全\Http\身份验证\SimplePreAuthenticatorInterface;ApiKeyAuthenticator实现了SimplePreAuthenticatorInterface{公共函数createToken(请求美元请求,美元providerKey){/ /寻找一个apikey查询参数美元apiKey=美元请求- >查询- >get (“apikey”);/ /或者如果你想使用一个“apikey”头,然后这样做:/ / apiKey =美元请求- >标题- >获取(“apiKey”);如果(!美元apiKey){BadCredentialsException ();/ /或者直接跳过api密钥身份验证/ /返回null;}返回PreAuthenticatedToken (“不久。”,美元apiKey,美元providerKey);}公共函数supportsToken(TokenInterface美元令牌,美元providerKey){返回美元令牌运算符PreAuthenticatedToken & &美元令牌- >getProviderKey () = = =美元providerKey;}公共函数authenticateToken(TokenInterface美元令牌,UserProviderInterface美元userProvider,美元providerKey){如果(!美元userProvider运算符ApiKeyUserProvider) {\ InvalidArgumentException (sprintf (的用户提供者必须实例ApiKeyUserProvider (% s)。”get_class (美元userProvider)));}美元apiKey=美元令牌- >getCredentials ();美元用户名=美元userProvider- >getUsernameForApiKey (美元apiKey);如果(!美元用户名){/ /注意:此消息将被返回给客户端/ /(所以不要把任何不受信任的消息/错误字符串)CustomUserMessageAuthenticationException (sprintf (API键“% s”是不存在的。”,美元apiKey));}美元用户=美元userProvider- >loadUserByUsername (美元用户名);返回PreAuthenticatedToken (美元用户,美元apiKey,美元providerKey,美元用户- >将getRoles ());}}

一旦你配置一切,你就可以通过添加一个验证apikey查询字符串参数,http://example.com/api/foo?apikey=37b51d194a7513e45b56f6524f2d51f2

身份验证过程有几个步骤,您的实现可能会有所不同:

1。createToken

在请求周期的早期,Symfony调用ob娱乐下载createToken ()。你的工作是创建一个令牌对象,其中包含的所有信息从请求,您需要验证用户(例如apikey查询参数)。如果这些信息丢失,扔一个BadCredentialsException将导致认证失败。您可能想要回报而不是直接跳过认证,所以Symfony可以回退到另一个身份验证方法,如果任何。ob娱乐下载

谨慎

如果你返回从你的createToken ()方法,确保启用匿名在你的防火墙。这样你就可以得到一个AnonymousToken

2。supportsToken

Symfonob娱乐下载y通电话后createToken (),它会调用supportsToken ()在你的类(和任何其他验证听众)找出谁应该处理令牌。这只是一种方式,允许多个身份验证机制用于相同的防火墙(例如,你可以首先尝试通过证书认证用户或一个API密匙和回到表单登录)。

大多数情况下,你只需要确保这个方法返回真正的的令牌已经创建的createToken ()。你的逻辑应该看起来就像这个例子。

3所示。authenticateToken

如果supportsToken ()返回真正的现在ob娱乐下载,Symfony会调用authenticateToken ()。是一个关键部分userProvider美元,这是一个外部类可以帮助你加载用户信息。您将了解更多关于下一个。

在这个特定的例子中,下面的事情发生authenticateToken ():

  1. 首先,您使用userProvider美元以某种方式查找美元的用户名相对应的apiKey美元;
  2. 第二,你使用userProvider美元再次加载或者创建一个用户对象的美元的用户名;
  3. 最后,您创建一个身份验证令牌(即一个令牌和至少一个角色),适当的角色和用户对象附加到它。

我们的目标是最终使用apiKey美元找到或创建一个用户对象。如何你这样做(如查询数据库)和为你的类用户对象可能会有所不同。这些差异将会在您的用户最明显的提供者。

用户提供者

userProvider美元可以是任何用户提供者(看到了吗如何创建一个自定义用户提供者)。在这个例子中,apiKey美元用于以某种方式找到用户的用户名。这项工作的完成getUsernameForApiKey ()方法,该方法创建完全自定义的用例(这不是一个方法,使用Symfony的核心用户提供系统)。ob娱乐下载

userProvider美元可能是这样的:

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
/ / src / AppBundle /安全/ ApiKeyUserProvider.php名称空间AppBundle\安全;使用ob娱乐下载\组件\安全\核心\用户\UserProviderInterface;使用ob娱乐下载\组件\安全\核心\用户\用户;使用ob娱乐下载\组件\安全\核心\用户\用户界面;使用ob娱乐下载\组件\安全\核心\异常\UnsupportedUserException;ApiKeyUserProvider实现了UserProviderInterface{公共函数getUsernameForApiKey(美元apiKey){/ /查找用户名在数据库中基于令牌,通过/ /一个API调用,或者做一些完全不同的美元用户名=……;返回美元用户名;}公共函数loadUserByUsername(美元用户名){返回用户(美元用户名,,/ /用户的角色——你可以选择来确定/ /这些动态基于用户数组(“ROLE_API”));}公共函数refreshUser(用户界面美元用户){/ /这是用于存储身份验证会话中/ /但在这个例子中,在每个请求发送令牌,/ /验证可以是无状态的。抛出这个异常/ /无状态是正确的事情UnsupportedUserException ();}公共函数supportsClass(美元){返回Sob娱乐下载ymfony的核心组件\ \安全\ \ \用户”= = =美元;}}

现在注册您的用户提供者作为服务:

  • YAML
  • XML
  • PHP
1 2 3 4
# app / config / services.yml服务:api_key_user_provider:类:AppBundle \安全\ ApiKeyUserProvider

请注意

阅读学习的专门的文章如何创建一个自定义用户提供者

里面的逻辑getUsernameForApiKey ()是由你决定。你可能会以某种方式转变API键(如。37 b51d)到一个用户名(如。jondoe通过查找一些信息在数据库表中“令牌”。

也是同样的道理loadUserByUsername ()。在这个例子中,Symfony的核心ob娱乐下载用户类是创建。这是有道理的,如果你不需要存储任何额外信息(如用户对象。firstName)。但是如果你这样做,你可能有你的自己的用户类的创建和填充这里通过查询数据库。这将允许你自定义数据用户对象。

最后,要确保supportsClass ()返回真正的相同的用户对象类无论您在返回的用户loadUserByUsername ()

如果你的认证是无状态的(如本例中(即你希望用户发送与每个请求API密匙,所以你不登录保存到会话),那么您可以简单地把UnsupportedUserException异常refreshUser ()

请注意

如果你想在会话中存储身份验证数据,关键不需要对每一个请求被发送,明白了如何验证用户身份的API密钥

处理身份验证失败

为了你的ApiKeyAuthenticator正确显示401年http状态糟糕的凭证或身份验证失败需要实现AuthenticationFailureHandlerInterface你的身份。这将提供一种方法onAuthenticationFailure你可以使用它来创建一个错误呢响应

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21日22日23日
/ / src / AppBundle /安全/ ApiKeyAuthenticator.php名称空间AppBundle\安全;使用ob娱乐下载\组件\安全\核心\异常\AuthenticationException;使用ob娱乐下载\组件\安全\Http\身份验证\AuthenticationFailureHandlerInterface;使用ob娱乐下载\组件\安全\Http\身份验证\SimplePreAuthenticatorInterface;使用ob娱乐下载\组件\HttpFoundation\响应;使用ob娱乐下载\组件\HttpFoundation\请求;ApiKeyAuthenticator实现了SimplePreAuthenticatorInterface,AuthenticationFailureHandlerInterface{/ /……公共函数onAuthenticationFailure(请求美元请求,AuthenticationException美元异常){返回响应(/ /这包含有关* *认证失败的原因/ /使用它,或返回自己的消息strtr (美元异常- >getMessageKey (),美元异常- >getMessageData ()),401年);}}

配置

一旦你有你的ApiKeyAuthenticator所有的设置,你需要注册一个服务和安全配置(如使用它。security.yml)。首先,作为一个服务注册它。

  • YAML
  • XML
  • PHP
1 2 3 4 5 6 7
# app / config / config.yml服务:#……apikey_authenticator:类:AppBundle \安全\ ApiKeyAuthenticator公众:

现在,激活它,您的自定义用户提供者(见如何创建一个自定义用户提供者)防火墙你的安全配置使用部分simple_preauth提供者键分别为:

  • YAML
  • XML
  • PHP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
# app / config / security.yml安全:#……防火墙:secured_area:模式:^ / api无状态:真正的simple_preauth:身份验证:apikey_authenticator供应商:api_key_user_provider提供者:api_key_user_provider:id:api_key_user_provider

如果你有定义access_control添加一个新条目,确保:

  • YAML
  • XML
  • PHP
1 2 3 4 5 6
# app / config / security.yml安全:#……access_control:- - - - - -{路径:^ / api,角色:ROLE_API}

就是这样!现在,您的ApiKeyAuthenticator应该被称为初每个请求和您的身份验证过程。

无状态的配置参数防止Symfony试图在会话中存储身份验证信息,这是没有必ob娱乐下载要的,因为客户端会发送apikey在每一个请求。如果你需要存储身份验证会话中,继续阅读!

在会话中存储身份验证

到目前为止,这个条目描述一种情况对每个请求发送某种形式的身份验证令牌。但在某些情况下(如OAuth流),令牌可以被发送一个请求。在这种情况下,您需要验证身份验证的用户和存储在会话中,这样用户自动登录之后的每一个请求。

做这项工作,请先删除无状态的从你的防火墙配置或设置为关键:

  • YAML
  • XML
  • PHP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
# app / config / security.yml安全:#……防火墙:secured_area:模式:^ / api无状态:simple_preauth:身份验证:apikey_authenticator供应商:api_key_user_provider提供者:api_key_user_provider:id:api_key_user_provider

即使令牌被存储在会话中,凭证——在这种情况下,API密匙(即。令牌- > getCredentials ())——不是存储在会话中出于安全原因。利用会话、更新ApiKeyAuthenticator存储标记是否有一个有效的用户对象,可以使用:

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
/ / src / AppBundle /安全/ ApiKeyAuthenticator.php/ /……ApiKeyAuthenticator实现了SimplePreAuthenticatorInterface{/ /……公共函数authenticateToken(TokenInterface美元令牌,UserProviderInterface美元userProvider,美元providerKey){如果(!美元userProvider运算符ApiKeyUserProvider) {\ InvalidArgumentException (sprintf (的用户提供者必须实例ApiKeyUserProvider (% s)。”get_class (美元userProvider)));}美元apiKey=美元令牌- >getCredentials ();美元用户名=美元userProvider- >getUsernameForApiKey (美元apiKey);/ /用户实体代表您的用户美元用户=美元令牌- >getUser ();如果(美元用户运算符用户){返回PreAuthenticatedToken (美元用户,美元apiKey,美元providerKey,美元用户- >将getRoles ());}如果(!美元用户名){/ /此消息将被返回给客户端CustomUserMessageAuthenticationException (sprintf (API键“% s”是不存在的。”,美元apiKey));}美元用户=美元userProvider- >loadUserByUsername (美元用户名);返回PreAuthenticatedToken (美元用户,美元apiKey,美元providerKey,美元用户- >将getRoles ());}/ /……}

在会话中存储身份验证信息是这样的:

  1. 在每个请求,Symfony序列化令牌对象(回来ob娱乐下载authenticateToken ()),这也序列化用户对象(因为它是设置在一个属性上的令牌);
  2. 在下一个请求令牌反序列化和反序列化用户对象传递给refreshUser ()用户提供的函数。

第二步是最重要的一个:Symfony的电话ob娱乐下载refreshUser ()并通过您的用户对象序列化的会话。如果你的用户存储在数据库中,那么您可能想要重新查询为一个新的版本的用户,以确保它不是过时的。但是不管你的需求,refreshUser ()现在应该返回用户对象:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
/ / src / AppBundle /安全/ ApiKeyUserProvider.php/ /……ApiKeyUserProvider实现了UserProviderInterface{/ /……公共函数refreshUser(用户界面美元用户){/ / $ user内设置的用户令牌authenticateToken ()/ /反序列化后的会话/ /用户可以使用美元为一个新的用户查询数据库/ / $ id = $ user - > getId ();/ /使用$ id查询/ /如果你是*不*阅读从数据库和创造/ /用户对象(如在本例中),你可以返回它返回美元用户;}}

请注意

你也会想要确保你的用户正确对象被序列化。如果你的用户对象具有私人性质,PHP不能序列化这些。在这种情况下,您可能会返回一个用户对象,有一个每个属性的值。例如,看到的如何从数据库中加载安全用户(实体提供者)

只对特定的url进行身份验证

这个条目已经假定你想要寻找的apikey上的身份验证每一个请求。但在某些情况下(如OAuth流),你只需要寻找身份验证信息一旦用户已经达到了一个特定的URL(如OAuth的重定向URL)。

幸运的是,处理这种情况下很容易:查看当前URL是什么之前创建的令牌createToken ():

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
/ / src / AppBundle /安全/ ApiKeyAuthenticator.php/ /……使用ob娱乐下载\组件\安全\Http\HttpUtils;使用ob娱乐下载\组件\HttpFoundation\请求;ApiKeyAuthenticator实现了SimplePreAuthenticatorInterface{受保护的美元httpUtils;公共函数__construct(HttpUtils美元httpUtils){美元- >httpUtils =美元httpUtils;}公共函数createToken(请求美元请求,美元providerKey){/ /设置唯一的URL,我们应该寻找身份验证信息/ /只返回令牌,如果我们在那个URL美元targetUrl=/登录/检查的;如果(!美元- >httpUtils- >checkRequestPath (美元请求,美元targetUrl)){返回;}/ /……}}

这使用方便HttpUtils类来检查当前URL是否你正在寻找匹配的URL。在本例中,URL (/登录/检查)已经被硬编码的类,但你也可以将它作为第二个构造函数的参数。

接下来,只是更新服务配置注入security.http_utils服务:

  • YAML
  • XML
  • PHP
1 2 3 4 5 6 7 8
# app / config / config.yml服务:#……apikey_authenticator:类:AppBundle \安全\ ApiKeyAuthenticator参数:(“@security.http_utils”)公众:

就是这样!玩得开心!

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