如何创建一个自定义的身份验证系统,保安吗
编辑该页面警告:你浏览的文档欧宝体育电话欧宝娱乐app下载地址Symfony 2.8,不再维护。
读这个页面的更新版本Symf欧宝娱乐app下载地址ony 6.3(当前的稳定版本)。
如何创建一个自定义的身份验证系统,保安吗
是否需要建立一个传统的登录表单,一个API令牌认证系统或需要与一些专有的单点登录系统集成,警卫组件可以很容易…和乐趣!
在这个例子中,您将构建一个API令牌认证系统和学习如何使用。
创建用户和用户提供者
无论你如何进行身份验证,您需要创建一个用户类实现用户界面
和配置用户提供者。在这个例子中,通过教义用户存储在数据库中,每个用户都有一个apiKey
他们使用通过API来访问自己的帐户:
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
/ / src / AppBundle /实体/ User.php名称空间AppBundle\实体;使用欧宝娱乐app下载地址\组件\安全\核心\用户\用户界面;使用学说\ORM\映射作为ORM;/ * * *@ORM* \实体@ORM\表(name = "用户”)* /类用户实现了用户界面{/ * * *@ORM\ Id *@ORM\ GeneratedValue(策略=“汽车”)*@ORM\列(类型=“整数”)* /私人美元id;/ * * *@ORM\列(type =“字符串”,独特的= true) * /私人美元用户名;/ * * *@ORM\列(type =“字符串”,独特的= true) * /私人美元apiKey;公共函数getUsername(){返回美元这- >用户名;}公共函数将getRoles(){返回数组(“ROLE_USER”);}公共函数getPassword(){}公共函数getSalt(){}公共函数eraseCredentials(){}/ /更多的getter / setter}
谨慎
在上面的示例中,表名用户
。这是一个SQL关键字和保留必须用引号引用避免错误的教义。你也可以改变表名(例如app_user
)来解决这个问题。
提示
这个用户没有密码,但是你可以添加一个密码
财产,如果你还想让这个用户登录和密码(例如通过一个登录表单)。
你的用户
类不需要存储在教义:做任何你需要的东西。接下来,确保你配置一个用户的“用户提供者”:
1 2 3 4 5 6 7 8 9 10 11
# app / config / security.yml安全:#……提供者:your_db_provider:实体:类:AppBundle:用户属性:apiKey#……
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
< !- - - - - -- - - - - -app/config/security.xml -->< /span>< ?xml version = " 1.0 " encoding = " utf - 8 " ? ><srv:容器xmlns=“http://欧宝娱乐app下载地址www.oldmanjams.com/schema/dic/security”xmlns: xsi=“http://www.w3.org/2001/XMLSchema-instance”xmlns:深水救生艇=“http://欧宝娱乐app下载地址www.oldmanjams.com/schema/dic/services”xsi: schemaLocation=“http://欧宝娱乐app下载地址www.oldmanjams.com/schema/dic/services //www.oldmanjams.com/schema/dic/services/services-1.0.xsd”><配置>< !- - - - - -- - - - - -。。。- - ><提供者的名字=“your_db_provider”><实体类=“AppBundle:用户”/ >< /提供者>< !- - - - - -- - - - - -。。。- - >< /配置>< /srv:容器>
1 2 3 4 5 6 7 8 9 10 11 12 13 14
/ / app / config / security.php美元容器- >loadFromExtension (“安全”,数组(/ /……“供应商”= >数组(“your_db_provider”= >数组(“实体”= >数组(“类”= >“AppBundle:用户”))),/ /……));
就是这样!关于这一步需要更多的信息,请参阅:
步骤1)创建身份验证类
假设您有一个API,你的客户将会发送一个X-AUTH-TOKEN
头在每个请求API令牌。你的工作是阅读这并找到相关的用户(如果有的话)。
创建一个自定义的身份验证系统,就创建一个类,使其实现GuardAuthenticatorInterface。或者,扩展更简单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
/ / src / AppBundle /安全/ TokenAuthenticator.php名称空间AppBundle\安全;使用欧宝娱乐app下载地址\组件\HttpFoundation\请求;使用欧宝娱乐app下载地址\组件\HttpFoundation\JsonResponse;使用欧宝娱乐app下载地址\组件\HttpFoundation\响应;使用欧宝娱乐app下载地址\组件\安全\核心\用户\用户界面;使用欧宝娱乐app下载地址\组件\安全\警卫\AbstractGuardAuthenticator;使用欧宝娱乐app下载地址\组件\安全\核心\身份验证\令牌\TokenInterface;使用欧宝娱乐app下载地址\组件\安全\核心\异常\AuthenticationException;使用欧宝娱乐app下载地址\组件\安全\核心\用户\UserProviderInterface;类TokenAuthenticator扩展AbstractGuardAuthenticator{/ * * *呼吁每一个要求。返回任何凭证你想*被传递给getUser ()。返回null会导致这个身份*跳过。* /公共函数getCredentials(请求美元请求){如果(!美元令牌=美元请求- >头- >get (“X-AUTH-TOKEN”)){/ /没有令牌?美元令牌=零;}/ /这里你还将被传递给getUser()美元凭证返回数组(“令牌”= >美元令牌,);}公共函数getUser(美元凭证,UserProviderInterface美元userProvider){美元apiKey=美元凭证(“令牌”];如果(零= = =美元apiKey){返回;}/ /如果用户对象,checkCredentials ()返回美元userProvider- >loadUserByUsername (美元apiKey);}公共函数checkCredentials(美元凭证,用户界面美元用户){/ /检查凭证——例如确保密码是有效的/ /不需要凭证检查/ /返回true导致身份验证成功返回真正的;}公共函数onAuthenticationSuccess(请求美元请求,TokenInterface美元令牌,美元providerKey){/ /成功,让请求继续比赛返回零;}公共函数onAuthenticationFailure(请求美元请求,AuthenticationException美元异常){美元数据=数组(“消息”= > strtr (美元异常- >getMessageKey (),美元异常- >getMessageData ())/ /或翻译这个消息/ / $ this - >翻译- >反式($例外- > getMessageKey(), $例外- > getMessageData ()));返回新JsonResponse (美元数据、响应::HTTP_FORBIDDEN);}/ * * *时调用身份验证是必要的,但它不是发送* /公共函数开始(请求美元请求,AuthenticationException美元authException= null){美元数据=数组(/ /你会翻译这个消息“消息”= >身份验证所需的);返回新JsonResponse (美元数据、响应::HTTP_UNAUTHORIZED);}公共函数supportsRememberMe(){返回假;}}
不错的工作!每个方法解释如下:警卫身份验证方法。
步骤2)配置身份验证
要完成这一点,注册类服务:
1 2 3 4
# app / config / services.yml服务:app.token_authenticator:类:AppBundle \安全\ TokenAuthenticator
1 2 3 4
< !- - - - - -- - - - - -app/config/services.xml -->< /span><服务><服务id=“app.token_authenticator”类=“AppBundle \安全\ TokenAuthenticator”/ >< /服务>
1 2 3 4 5 6
/ / app / config / services.php使用AppBundle\安全\TokenAuthenticator;使用欧宝娱乐app下载地址\组件\DependencyInjection\定义;使用欧宝娱乐app下载地址\组件\DependencyInjection\参考;美元容器- >注册(“app.token_authenticator”,TokenAuthenticator::类);
最后,配置您的防火墙
关键在security.yml
使用这个身份:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
# app / config / security.yml安全:#……防火墙:#……主要:匿名:~注销:~警卫:身份验证器:- - - - - -app.token_authenticator#如果你想禁用用户存储在会话中无状态:真正的#或许其他的东西,像form_login, remember_me等等#……
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21日22日23日24
< !- - - - - -- - - - - -app/config/security.xml -->< /span>< ?xml version = " 1.0 " encoding = " utf - 8 " ? ><srv:容器xmlns=“http://欧宝娱乐app下载地址www.oldmanjams.com/schema/dic/security”xmlns: xsi=“http://www.w3.org/2001/XMLSchema-instance”xmlns:深水救生艇=“http://欧宝娱乐app下载地址www.oldmanjams.com/schema/dic/services”xsi: schemaLocation=“http://欧宝娱乐app下载地址www.oldmanjams.com/schema/dic/services //www.oldmanjams.com/schema/dic/services/services-1.0.xsd”><配置>< !- - - - - -- - - - - -。。。- - ><防火墙的名字=“主要”模式=“^ /”匿名=“真正的”><注销/ ><警卫><身份验证>app.token_authenticator< /身份验证>< /警卫>< !- - - - - -- - - - - -。。。- - >< /防火墙>< /配置>< /srv:容器>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/ / app / config / security.php/ / . .美元容器- >loadFromExtension (“安全”,数组(“防火墙”= >数组(“主要”= >数组(“模式”= >' ^ /,“匿名”= >真正的,“注销”= >真正的,“警卫”= >数组(“身份验证器”= >数组(“app.token_authenticator”),),/ /……))));
你做到了!您现在拥有了一个有效的API令牌认证系统。如果你的首页需要ROLE_USER
,然后你可以测试它在不同条件下:
1 2 3 4 5 6 7 8 9 10 11
#测试没有令牌curl http://localhost: 8000 /#{“消息”:“身份验证需要“}#测试坏牌curl - h“X-AUTH-TOKEN:假”http://localhost: 8000 /#{“消息”:“用户名无法找到。”}#测试工作牌curl - h“X-AUTH-TOKEN:真正的”http://localhost: 8000 /#主页控制器执行:页面加载正常
现在,了解更多关于什么每个方法。
警卫身份验证方法
每个身份需要以下方法:
- 美元getCredentials(请求请求)
-
这将是呼吁每一个请求和你的工作是阅读令牌(或无论你“身份验证”信息)从请求并返回。如果你返回
零
身份验证过程的,其余是跳过。否则,getUser ()
将调用和返回值是作为第一个参数传递。 - getUser(美元凭证,UserProviderInterface userProvider美元)
-
如果
getCredentials ()
返回一个null值,然后调用此方法,它的返回值是通过这里美元的凭证
论点。你的工作是实现返回一个对象用户界面
。如果你这样做,checkCredentials ()
将被调用。如果你返回零
(或者抛出一个AuthenticationException身份验证失败。 - checkCredentials(凭证美元,用户界面用户)
-
如果
getUser ()
返回一个用户对象,调用此方法。你的工作是验证凭证是正确的。对于一个登录表单,这就是你会检查用户的密码是否正确。通过身份验证、回报真正的
。如果你返回任何东西其他(或抛出AuthenticationException),身份验证就会失败。 - onAuthenticationSuccess(请求请求美元,美元TokenInterface令牌,providerKey美元)
-
这就是所谓的成功的身份验证之后,你的工作是返回一个响应对象将被发送到客户端
零
继续请求(例如,允许路线/控制器被称为像正常)。因为这是一个API,每个请求进行身份验证本身,你想回来零
。 - onAuthenticationFailure(请求请求美元,美元AuthenticationException除外)
-
这就是所谓的如果认证失败。你的工作是回报响应对象,应发送给客户机。的
美元的例外
会告诉你什么在身份验证错误。 - 开始(请求$请求,AuthenticationException authException美元= null)
-
这叫做如果客户机访问一个URI /资源,需要认证,但是没有发送验证信息(即你返回
零
从getCredentials ()
)。你的工作是返回一个响应对象,帮助用户进行身份验证(例如,一个401响应说,“令牌丢失!”)。 - supportsRememberMe ()
-
如果你想支持“记住我”功能,从这个方法返回true。你仍然需要激活
remember_me
在你的防火墙工作。由于这是一个无状态的API,您不想在这个例子中支持“记住我”功能。 - createAuthenticatedToken(用户界面用户美元,字符串providerKey美元)
- 如果你实现了GuardAuthenticatorInterface而不是扩展AbstractGuardAuthenticator类,您必须实现这个方法。它将被称为成功的身份验证之后创建并返回用户的令牌,作为第一个参数提供。
下图显示了如何Symfony调用后卫身份验证方法:欧宝娱乐app下载地址
自定义错误信息
当onAuthenticationFailure ()
被调用时,通过了吗AuthenticationException
描述如何验证未通过$例外- > getMessageKey ()
(和$例外- > getMessageData ()
)方法。消息将被不同的基础上在哪里身份验证失败(即。getUser ()
与checkCredentials ()
)。
但是,您可以轻松地返回一个自定义消息通过投掷CustomUserMessageAuthenticationException。你可以把这个getCredentials ()
,getUser ()
或checkCredentials ()
导致一个失败:
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 / AppBundle /安全/ TokenAuthenticator.php/ /……使用欧宝娱乐app下载地址\组件\安全\核心\异常\CustomUserMessageAuthenticationException;类TokenAuthenticator扩展AbstractGuardAuthenticator{/ /……公共函数getCredentials(请求美元请求){/ /……如果(美元令牌= =“ILuvAPIs”){扔新CustomUserMessageAuthenticationException (ILuvAPIs不是一个真正的API密匙:\ ' s只是一个愚蠢的说法“);}/ /……}/ /……}
在这种情况下,由于“ILuvAPIs”是一个荒谬的API密匙,你可能包括一个复活节彩蛋返回一个自定义消息如果有人尝试:
1 2
curl - h“X-AUTH-TOKEN: ILuvAPIs”http://localhost: 8000 /#{“消息”:“ILuvAPIs不是一个真正的API键:这只是一个愚蠢的短语“}
避免对浏览器对每个请求进行身份验证
如果你创建一个后卫登录系统使用的浏览器,你正在经历的问题与你的会话或CSRF令牌,你的身份的原因可能是不良行为。当一个后卫身份验证是使用一个浏览器,你应该不认证用户每一个请求。换句话说,你需要确保getCredentials ()
方法只有当你返回一个空值需要对用户进行身份验证。为什么?因为,当getCredentials ()
返回一个null值,为安全目的,用户的会话是“迁移”到一个新的会话id。
这是一个极端例子,除非你有会话或CSRF令牌的问题,你可以忽略这一点。这是好的和坏的行为的一个例子:
1 2 3 4 5 6 7 8 9 10 11 12
公共函数getCredentials(请求美元请求){/ /良好的行为:只有特定路线上进行身份验证如果(美元请求- >属性- >get (“_route”)= = !“login_route”| | !美元请求- >isMethod (“职位”)){返回零;}/ /例如登录系统进行身份验证的用户的IP地址/ /不良行为:身份验证将在每个请求执行/ /即使用户已经通过身份验证(由于会话)返回数组(“知识产权”= >美元请求- >getClientIp ());}
这个问题发生在你的浏览器试图验证用户身份每一个请求——就像在前面的例子基于IP地址。有两种可能的修复:
- 如果你做不需要存储在会话中,身份验证设置
无状态:真
在你的防火墙。 - 更新你的身份,以避免认证如果用户已经通过身份验证:
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日
/ / src /安全/ MyIpAuthenticator。php / /……+使用Sym欧宝娱乐app下载地址fony组件\ \安全\ \认证\牌\核心存储\ TokenStorageInterface;类MyIpAuthenticator {+私人tokenStorage美元;+公共职能__construct (TokenStorageInterface tokenStorage美元)+ {+ $ this - > tokenStorage = $ tokenStorage;+}公共函数getCredentials(请求请求美元){+ / /如果已经有一个身份验证的用户(可能由于会话)+ / /然后返回null,跳过认证:没有必要。+ $ user = $ this - > tokenStorage - > getToken () ?$ this - > tokenStorage - > getToken () - > getUser():空;+如果(is_object(用户)美元){+返回null;+}返回数组(“知识产权”= > $请求- > getClientIp ());}}
您还需要更新您的服务配置传递令牌存储:
1 2 3 4 5
# app / config / services.yml服务:app.token_authenticator:类:AppBundle \安全\ TokenAuthenticator参数:(“@security.token_storage”)
1 2 3 4 5 6
< !- - - - - -- - - - - -app/config/services.xml -->< /span><服务><服务id=“app.token_authenticator”类=“AppBundle \安全\ TokenAuthenticator”><论点类型=“服务”id=“security.token_storage”/ >< /服务>< /服务>
1 2 3 4 5 6 7
/ / app / config / services.php使用AppBundle\安全\TokenAuthenticator;使用欧宝娱乐app下载地址\组件\DependencyInjection\定义;使用欧宝娱乐app下载地址\组件\DependencyInjection\参考;美元容器- >注册(“app.token_authenticator”,TokenAuthenticator::类)- >addArgument (新引用(“security.token_storage”));
常见问题
- 我可以有多个身份验证器吗?
-
是的!但是当你做什么,你需要选择一个身份验证是你“entry_point”。这意味着你需要选择哪一个身份验证的
start ()
方法时应该调用一个匿名用户试图访问受保护的资源。例如,假设您有一个app.form_login_authenticator
处理一个传统形式登录。当一个匿名用户访问一个受保护的页面,你想使用start ()
从表单身份验证方法,将他们重定向到登录页面(而不是返回一个JSON响应):1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
# app / config / security.yml安全:#……防火墙:#……主要:匿名:~注销:~警卫:entry_point:app.form_login_authenticator身份验证器:- - - - - -app.token_authenticator- - - - - -app.form_login_authenticator#如果你想禁用用户存储在会话中无状态:真正的#或许其他的东西,像form_login, remember_me等等#……
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
< !- - - - - -- - - - - -app/config/security.xml -->< /span>< ?xml version = " 1.0 " encoding = " utf - 8 " ? ><srv:容器xmlns=“http://欧宝娱乐app下载地址www.oldmanjams.com/schema/dic/security”xmlns: xsi=“http://www.w3.org/2001/XMLSchema-instance”xmlns:深水救生艇=“http://欧宝娱乐app下载地址www.oldmanjams.com/schema/dic/services”xsi: schemaLocation=“http://欧宝娱乐app下载地址www.oldmanjams.com/schema/dic/services //www.oldmanjams.com/schema/dic/services/services-1.0.xsd”><配置>< !- - - - - -- - - - - -。。。- - ><防火墙的名字=“主要”模式=“^ /”匿名=“真正的”><注销/ ><警卫入口点=“app.form_login_authenticator”><身份验证>app.token_authenticator< /身份验证><身份验证>app.form_login_authenticator< /身份验证>< /警卫>< !- - - - - -- - - - - -。。。- - >< /防火墙>< /配置>< /srv:容器>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
/ / app / config / security.php/ / . .美元容器- >loadFromExtension (“安全”,数组(“防火墙”= >数组(“主要”= >数组(“模式”= >' ^ /,“匿名”= >真正的,“注销”= >真正的,“警卫”= >数组(“entry_point”= >“app.form_login_authenticator”,“身份验证器”= >数组(“app.token_authenticator”,“app.form_login_authenticator”),),/ /……))));
- 我可以使用这个与form_login吗?
-
是的!
form_login
是一个验证一个用户,所以你可以使用它和然后添加一个或多个身份验证器。使用一个后卫身份不与其他方法来验证相撞。 - 我可以使用这个与FOSUserBundle吗?
-
是的!实际上,FOSUserBundle不处理安全:它只是给你一个
用户
对象和一些路线和控制器帮助登录、注册、忘记密码,等。使用FOSUserBundle时,你通常使用form_login
实际用户进行身份验证。你可以继续这样做(见以前的问题)或使用用户
对象从FOSUserBundle和创建自己的身份(s)(就像在本文中)。