(Security)如何使用Guard创建自定义的身份验证系统(2.8)

无论你是构建一个传统的登录表单,还是一个api token验证系统或者是你需要集成一些专有的单点登录系统,Guard组件能够很容易的做这些事情….并且很有趣!

在这个例子中,你需要构建一个api token验证系统并且学习如何使用Guard来完成工作。

创建一个User和一个User Provider

不管你如何验证,你都需要创建一个User类并实现UserInterface并配置一个 user provider.在本例中,用户使用doctrine存储在数据库中,并且每个用户都有一个apikey属性,使用这个api来访问他们的账户:

该用户没有密码,如果你想允许这个用户登录时使用密码,你可以添加一个password属性(在表单登陆中会用到)

你的User类不一定要存储在Doctrine:看你的需要。

下一步,确保你配置了用户的”user provider”:

然而!你想了解关于这些的更多信息,请查阅:

 

步骤1)创建Authenticator类

假设你有一个api,你的客户端将针对每个请求发送一个X-AUTH-TOKEN头并跟随API token。你的任务就是读取他们并找到相关用户(如果他们有的话)。

创建一个自定义验证系统,仅仅需要创建一个类并让他实现 GuardAuthenticatorInterface.或者继承这个更简单的AbstractGuardAuthenticator.他需要你去实现六个方法。

干的好!下面有每种方法的解释(Guard 验证方法)

 

步骤2)配置这个验证

要完成他,需要注册这个类为一个服务:

最后,在security.yml中配置你的firewalls键来开启并使用这个验证:

你做到了!你现在有一个有效的api token 验证系统了。如果你的首页需要ROLE_USER,那么你可以在不同条件下进行测试:

现在,开始了解每一个方法。

 

Guard 验证方法

每个验证都需要一下方法:

getCredentials(Request $request)

他会获得每一个请求,你要从中获得token(或者任何你身份验证的信息)并将其返回。如果返回null,其余的验证部分就会被跳过。否则getUser()将会被调用并且返回值会作为第一个参数。

getUser($credentials, UserProviderInterface $userProvider)

如果getCredentials()返回一个非空值,则此方法会被调用,并传入这个返回值$credentials作为参数。你的任务是实现UserInterface并返回一个对象。如果你这么做那么checkCredentials()就会被调用。如果返回null(或者抛出AuthenticationException)验证宣告失败。

checkCredentials($credentials, UserInterface $user)

如果getUser成功返回一个User对象,该方法就会被调用。你的任务是验证credentials是否正确。对于登陆表单,在这里会检查用户的密码是否正确。如果通过验证,则返回true。如果你返回其他的东西(或者抛出AuthenticationException)验证失败。

onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)

这个会在成功授权之后调用,你的任务是返回一个Response对象并发送到客户端,或者返回null继续这个请求(例如,通常会允许请求的路由器/控制器继续被调用)。由于这个是一个api,每个请求都会自我验证,你可能回去返回null。

onAuthenticationFailure(Request $request, AuthenticationException $exception)

如果验证失败会返回这个方法。你的工作是返回一个Response对象并发送到客户端。这个 $exception将告诉你什么验证过程出的错。

start

如果客户端访问一个需要验证的URI/资源他就会被调用,但没有发送验证细节信息(例如,你从getCredentials()返回null)。你的任务是返回一个Response对象帮助用户验证(例如 一个401相应,上面写着“令牌丢了”)。

supportsRememberMe

如果你希望支持“remember me”功能,这个方法就要返回true。你仍需要在防火墙下启用rememebe_me才能工作。由于这是一个无状态的api,你在这个例子中就不需要支持remember me功能了。

 

自定义错误信息

当onAuthenticationFailure()被调用,他就会传入一个AuthenticationException,可以使用$e->getMessageKey()(或$e->getMessageData())方法来描述你怎样验证失败的。这些信息将基于不同的验证失败产生(例如getUser与checkCredentials())。

但是,你也可以很容易的返回一个自定义的消息使用CustomUserMessageAuthenticationException。你能够抛出由getCredentials(), getUser() 和 checkCredentials()导致的失败:

这种情况下,由于”ILuvAPIs”是一个荒唐的api键,如果有人试图这样你可以返回一个“复活节的彩蛋”消息

 

常见问题

我们可以有多个验证吗?

可以!但是当你这样做时,你需要选择一个验证的“entry_point”.这意味着当一个匿名用户试图访问受保护的资源时,你需要选择验证应该调用的start方法。例如,假设你有一个app.form_login_authenticator来处理传统的登陆。当匿名用户访问受保护的页面时,你就要使用form authenticator的start()方法,并将其重定向到登陆页面(而不是返回JOSN响应):

我可以使用“form_login”吗?

可以!form_login是一种验证方式,所以你可以使用它,并还可以添加更多的验证。使用一个guard authenticator并不与其他的验证方法相冲突。

你可以使用FOSUserBundle?

可以!其实,FOSUserBundle不处理安全:他只是给你一个用户对象和一些路由和控制器,以帮助你完成登陆,注册,忘记密码等,当你使用FOSUserBundle,您通常使用form_login来验证身份。你可以继续这样做(参考前面的问题),或使用FOSUserBundle的User对象并创建我们自己的验证(就像本章一样)。

 

发表评论