带Guard的自定义认证系统(API令牌示例)

编辑本页

警告:您正在浏览的文档欧宝体育电话欧宝娱乐app下载地址Symfony 5.0,现已不再维护。

本页的更新版本用于Sy欧宝娱乐app下载地址mfony 6.2(当前稳定版本)。

带Guard的自定义认证系统(API令牌示例)

Guard身份验证可用于:

还有更多。在本例中,我们将构建一个API令牌身份验证系统,这样我们就可以更详细地了解Guard。

步骤1)准备你的用户类

假设您想要构建一个API,您的客户端将在其中发送一个X-AUTH-TOKEN头和它们的API令牌。您的工作是读取该文件并找到相关的用户(如果有的话)。

首先,确保你遵循了主要原则安全指南要创建您的用户类。然后,为了保持简单,添加一个apiToken财产直接交给你的用户类(:实体Command是一个很好的方法):

12 3 4 5 6 7 8 9 10 11 12 13 14
// src/Entity/User.php…类用户实现UserInterface{//…+ / * *+ * @ORM\Column(type="string", unique=true, nullable=true)+ * /+ private $apiToken;// getter和setter方法}

不要忘记生成和执行迁移:

1 2
PHP bin/控制台make:迁移PHP bin/控制台原则:迁移:迁移

步骤2)创建Authenticator类

要创建自定义身份验证系统,请创建一个类并使其实现AuthenticatorInterface.或者,扩展更简单的AbstractGuardAuthenticator

这需要你实现几个方法:

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 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
/ / src /安全/ TokenAuthenticator.php名称空间应用程序安全使用应用程序实体用户使用学说ORMEntityManagerInterface使用欧宝娱乐app下载地址组件HttpFoundationJsonResponse使用欧宝娱乐app下载地址组件HttpFoundation请求使用欧宝娱乐app下载地址组件HttpFoundation响应使用欧宝娱乐app下载地址组件安全核心身份验证令牌TokenInterface使用欧宝娱乐app下载地址组件安全核心异常AuthenticationException使用欧宝娱乐app下载地址组件安全核心用户用户界面使用欧宝娱乐app下载地址组件安全核心用户UserProviderInterface使用欧宝娱乐app下载地址组件安全警卫AbstractGuardAuthenticatorTokenAuthenticator扩展AbstractGuardAuthenticator私人新兴市场公共函数__construct(EntityManagerInterface新兴市场->em =新兴市场;}/** *在每个请求上调用,以决定是否应该为请求使用这个验证器。返回' false '将导致这个验证器*被跳过。* /公共函数支持(请求请求返回请求->->有(“X-AUTH-TOKEN”);}/** *在每个请求时调用。返回您想要传递给getUser()的任何凭据作为$credentials。* /公共函数getCredentials(请求请求返回请求->->get (“X-AUTH-TOKEN”);}公共函数getUser凭证, UserProviderInterfaceuserProvider如果= = =凭证) {//标记头为空,HTTP状态下认证失败//代码401 "未经授权"返回;}//如果返回User,则调用checkCredentials()返回->新兴市场->getRepository(用户::类)->findOneBy ([“apiToken”= >凭证]);}公共函数checkCredentials凭证,用户界面用户//检查凭证-例如,确保密码有效。//如果是API令牌,则不需要凭据检查。//返回' true '导致认证成功返回真正的;}公共函数onAuthenticationSuccess(请求请求, TokenInterface令牌providerKey//如果成功,让请求继续返回;}公共函数onAuthenticationFailure(请求请求, AuthenticationException异常数据= (//你可能想先自定义或模糊消息“消息”= > strtr (异常->getMessageKey (),异常->getMessageData ())//或翻译此消息// $this->translator->trans($exception->getMessageKey(), $exception->getMessageData())];返回JsonResponse (数据、响应::HTTP_UNAUTHORIZED);}/** *需要身份验证时调用,但不发送*/公共函数开始(请求请求, AuthenticationExceptionauthException= null)数据= (//你可以翻译这条信息“消息”= >身份验证所需的];返回JsonResponse (数据、响应::HTTP_UNAUTHORIZED);}公共函数supportsRememberMe()返回;}}

不错的工作!每种方法解释如下:守卫验证器方法

步骤3)配置验证方

要完成此操作,请确保您的验证程序已注册为服务。如果你在用默认的服务。yaml的配置,这是自动发生的。

最后,配置防火墙关键在security.yaml使用此验证器:

  • YAML
  • XML
  • PHP
12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
#配置/包/ security.yaml安全:#……防火墙:#……主要:匿名:懒惰的注销:警卫:身份验证器:-App \安全\ TokenAuthenticator#如果你愿意,禁用在会话中存储用户#无状态:true#……

你做到了!现在您已经有了一个正常工作的API令牌身份验证系统。如果需要你的主页ROLE_USER,然后可以在不同的条件下进行测试:

1 2 3 4 5 6 7 8 9 10 11
#测试没有标记curl http://localhost: 8000 /# {"message":"Authentication Required"}#测试一个坏的标记curl - h“X-AUTH-TOKEN:假”http://localhost:8000/# {"message":"无法找到用户名。"}使用一个工作令牌进行测试curl - h“X-AUTH-TOKEN:真正的”http://localhost:8000/#主页控制器执行:页面正常加载

现在,进一步了解每种方法的功能。

守卫验证器方法

每个验证方需要使用以下方法:

支持请求(请求)
这个被调用了<新兴市场>每一个请求,您的工作是决定是否应该将验证器用于此请求(返回真正的)或者是否应该跳过(返回).
美元getCredentials(请求请求)
您的工作是从请求中读取令牌(或任何“身份验证”信息)并返回它。这些凭证被传递给getUser ()
getUser($credentials, UserProviderInterface $userProvider)
美元的凭证参数返回的值getCredentials ().您的任务是返回一个实现的对象用户界面.如果你愿意,那么checkCredentials ()将被调用。如果你回来(或者扔一个AuthenticationException)验证将失败。
checkCredentials($credentials, UserInterface $user)
如果getUser ()返回一个User对象,此方法被调用。你的工作是验证凭证是否正确。对于登录表单,这是检查用户密码是否正确的地方。若要通过认证,返回真正的.如果你回来(或者扔一个AuthenticationException),验证将失败。
onAuthenticationSuccess(请求$ Request, TokenInterface $token,字符串$providerKey)
这是在成功验证后调用的,您的工作是返回响应对象,该对象将被发送到客户机或继续请求(例如,允许像正常一样调用路由/控制器)。由于这是一个每个请求都要对自己进行身份验证的API,所以您希望返回
onAuthenticationFailure(请求$ Request, AuthenticationException $exception)
如果身份验证失败,则调用此方法。你的任务是归还响应应该发送给客户端的对象。的美元的例外会告诉你<新兴市场>什么身份验证时出错。
开始(请求$请求,AuthenticationException $authException = null)
如果客户端访问需要身份验证的URI/资源,但没有发送身份验证详细信息,则调用此方法。你的任务是返回一个响应对象,帮助用户进行身份验证(例如,表示“令牌缺失!”的401响应)。
supportsRememberMe ()
如果您想支持“remember me”功能,请返回真正的从这个方法。你仍然需要激活remember_me在你的防火墙下才能生效。由于这是一个无状态API,因此在本例中不希望支持“remember me”功能。
createAuthenticatedToken(UserInterface $user,字符串$providerKey)
如果您正在实现AuthenticatorInterface而不是扩展AbstractGuardAuthenticator类时,您必须实现此方法。在成功的身份验证之后,将调用它来创建和返回令牌(类实现GuardTokenInterface),作为第一个参数提供给用户。

下图显示了Symfony如何调用Guard Authen欧宝娱乐app下载地址ticator方法:

自定义错误消息

onAuthenticationFailure ()是调用,它是传递一个AuthenticationException描述<新兴市场>如何通过其异常- > getMessageKey ()(和异常- > getMessageData ())方法。信息会因不同而不同<新兴市场>在哪里身份验证失败(即getUser ()checkCredentials ()).

方法来返回自定义消息CustomUserMessageAuthenticationException.你可以把这个扔出去getCredentials ()getUser ()checkCredentials ()导致失败:

12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
/ / src /安全/ TokenAuthenticator.php/ /……使用欧宝娱乐app下载地址组件安全核心异常CustomUserMessageAuthenticationExceptionTokenAuthenticator扩展AbstractGuardAuthenticator/ /……公共函数getCredentials(请求请求/ /……如果令牌= =“ILuvAPIs”) {CustomUserMessageAuthenticationException (“ILuvAPIs不是真正的API密钥:它只是一个愚蠢的短语”);}/ /……/ /……

在这种情况下,由于"ILuvAPIs"是一个可笑的API键,如果有人尝试这样做,你可以包括一个彩蛋来返回一个自定义消息:

1 2
curl - h“X-AUTH-TOKEN: ILuvAPIs”http://localhost:8000/# {"message":"ILuvAPIs不是一个真正的API键:它只是一个愚蠢的短语"}

手动认证用户

有时您可能希望手动验证用户——比如在用户完成注册之后。要做到这一点,请使用您的验证程序和名为GuardAuthenticatorHandler

12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/ / src /控制器/ RegistrationController.php/ /……使用应用程序安全LoginFormAuthenticator使用欧宝娱乐app下载地址组件HttpFoundation请求使用欧宝娱乐app下载地址组件安全警卫GuardAuthenticatorHandlerRegistrationController扩展AbstractController公共函数注册(LoginFormAuthenticator身份验证, GuardAuthenticatorHandlerguardHandler,请求请求/ /……//在验证用户并将其保存到数据库后//验证用户并在验证方上使用onAuthenticationSuccess返回guardHandler->authenticateUserAndHandleSuccess (用户//你刚刚创建的User对象请求身份验证//要使用onAuthenticationSuccess的验证器“主要”//你的防火墙在security.yaml中的名称);}}

避免对每个请求都进行浏览器认证

如果您创建了一个浏览器使用的Guard登录系统,而您的会话或CSRF令牌遇到了问题,则原因可能是验证器的不良行为。当Guard身份验证器要由浏览器使用时,您应该这样做<新兴市场>不验证用户的身份<新兴市场>每一个请求。换句话说,你需要确保支持()方法<新兴市场>只有返回真正的当你真的<新兴市场>需要验证用户身份。为什么?因为,当支持()返回true(身份验证最终成功),出于安全考虑,用户的会话被“迁移”到一个新的会话id。

这是一个边缘情况,除非您遇到会话或CSRF令牌问题,否则可以忽略它。下面是一个好行为和坏行为的例子:

1 2 3 4 5 6 7 8 9 10
公共函数支持(请求请求//良好行为:只在特定路由上验证(即返回true)返回“login_route”= = =请求->属性->get (“_route”) & &请求->isMethod (“职位”);例如,你的登录系统通过用户的IP地址进行认证//坏行为:所以,你决定*always*返回true//你可以在每个请求上检查用户的IP地址返回真正的;}

当基于浏览器的身份验证程序试图对用户进行身份验证时,就会出现问题<新兴市场>每一个类似于上面基于IP地址的例子中的request。有两种可能的解决方法:

  1. 如果有的话<新兴市场>不需要身份验证存储在会话中,设置无状态:真在你的防火墙下。
  2. 如果用户已经通过身份验证,更新身份验证器以避免身份验证:
12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
// src/Security/ myipauthentication .php //…+使用Sym欧宝娱乐app下载地址fony\Component\Security\Core\Security;MyIpAuthenticator类+私人$安全;+公共函数__construct(Security $ Security)+ {+ $this->security = $security;+}公共功能支持(请求$请求){+ //如果已经有一个身份验证的用户(可能是由于会话)+ //然后返回false并跳过认证:不需要。+ if ($this->security->getUser()) {+返回false;+}+ //用户没有登录,所以验证程序应该继续+返回true;}}

如果使用自动装配,则安全服务将自动传递给您的验证程序。

常见问题

我可以拥有多个身份验证器吗?
是的!但当你这么做的时候,你需要选择<新兴市场>一个验证器作为您的“entry_point”。这意味着你需要做出选择<新兴市场>哪一个身份验证的start ()方法在匿名用户试图访问受保护资源时调用。详情请参见如何使用多个Guard认证器
我可以使用这个form_login吗?
是的!form_login是<新兴市场>一个验证用户身份的方法,这样您就可以使用它<新兴市场>而且然后添加一个或多个身份验证器。使用保护身份验证器不会与其他身份验证方法冲突。
我可以用FOSUserBundle吗?
是的!实际上,FOSUserBundle并不处理安全性:它只是给您一个用户对象和一些路由和控制器来帮助登录、注册、忘记密码等。当您使用FOSUserBundle时,您通常使用form_login实际验证用户。您可以继续这样做(参见前一个问题)或使用用户从FOSUserBundle中创建一个或多个验证器(就像本文中一样)。
此工作,包括代码示例,是根据创作共用BY-SA 3.0许可证。