第十三章:安全Security

Security(安全)的目的是防止用户访问他们不应该访问的资源,需要两个步骤实现这个过程。

该方法的第一个步骤中,知道该安全系统识别用户是谁,要求用户提交某种身份识别。这就是所谓的authentication(认证),这意味着该系统试图找出你是谁。
一旦系统知道了你是谁,进入下一个步骤来确定是否能够访问指定的资源。这个过程被称为authorization(授权),它的意思是系统正在检查,看看是否有权限来执行某一动作。
security_authentication_authorization
最好的学习方法是看一个例子,假设你想在您的应用程序中使用HTTP基本身份验证。

Symfony2的安全组件作为一个独立的PHP库可以用于任何PHP项目中。

基本的例子:HTTP Authentication(认证)

安全组件可以通过应用程序配置进行配置。实际上,最标准的安全设置只是使用了正常的配置。
下面的配置是告诉symfony,对于任何匹配 /admin/* 的URL都需要保护,并要求使用基本的HTTP Authentication(认证)(即老派的用户名/密码对话框)来询问用户凭证。

 一个标准的Symfony2发布将安全配置单独放入单个文件(如:app/config/security.yml)。如果你没有单独的安全文件,你可以直接将配置放入你的主配置文件中(如app/config/config.yml)。

该配置最终结果是一个全功能的安全系统,如下所示:

  • 有两个用户在系统中(ryan和admin);
  • 用户可以通过基本的HTTP身份验证提示来验证自己;
  • 任何匹配/admin/*的URL都会经过安全认证,只有admin用户可以访问它;
  • 所有不匹配/admin/*的URL可以被所有用户访问(永远不会提示用户登录);

让我们简要地看看安全是如何工作的,以及配置的每一部分是如何在一起工作的。

Security怎样工作:认证和授权

Symfony2安全系统的工作就是确定用户是谁(认证)然后检查看看用户是否有访问特定资源或URL的权限。

防火墙(认证)

当一个用户将一个请求发向一个被防火墙保护的URL时,安全系统就被激活了。防火墙的工作就是要确保用户是不是需要被认证,如果需要的话,发送一个响应返回给用户去启动认证过程。
当传入请求的URL匹配防火墙的正则表达式pattern(模式)时,防火墙被激活。在本例中,pattern(模式) (^/)将匹配任何传入的请求。然而,其实防火墙被激活也没有意义,任何URL都不会使HTTP认证用户名和密码对话框出现。例如,任何用户都可以访问/foo,而不会提示要去认证。

您也可以匹配的请求的其他信息(如主机和方法)的请求。欲了解更多信息和示例,阅读 何限制防火墙到一个特定的请求

security_anonymous_user_access
之所以这样首先是因为通过anonymous(匿名)配置参数,防火墙允许匿名用户通过。换句话说,防火墙不要求用户立刻进行认证。其次是因为没有特定的role(角色)需要访问/foo(下面的access_control部分),请求甚至可以在没有要求用户认证的情况下完成。
如果你删除了anonymous(匿名)关键词,那么防火墙总是要求用户立即认证的。

访问控制(授权)

然而如果用户请求/admin/foo,那么处理过程是不同的。这是因为access_control配置部指出任何匹配正则表达式^/admin(如/admin或任何匹配/admin/*的URL)都需要ROLE_ADMIN角色。角色是大多数授权的基础:如果用户拥有ROLE_ADMIN角色,用户可以访问/admin/foo。
security_anonymous_user_denied_authorization
像前面一样,当用户最初发送请求时,防火墙并不要求任何身份。然而当访问控制层拒绝用户访问(因为匿名用户没有ROLE_ADMIN用户)时,防火墙接管该过程,并启动认证进程。认证依赖你所用的认证机制。例如,如果你使用表单登录认证方式,用户将被重定向到登录页面。如果你使用的是HTTP认证,用户将被发送HTTP的401响应,以便用户可以看到用户名密码对话框。
用户现在有机会提交它的证书给应用程序。如果证书是有效的,那么原始请求会被重发。
security_ryan_no_role_admin_access
在本例中,用户ryan成功通过防火墙认证。但因为ryan没有拥有ROLE_ADMIN角色,它仍然被拒绝访问/admin/foo。最终这意味着用户将看到消息说明访问被拒绝。
当Symfony2拒绝用户访问时,用户可以看到一个错误页面并收到一个HTTP的403状态码(Forbidden)。你可以根据食谱(cookbook)错误页面中的自定义403错误页内容来自定义拒绝访问页。

最后,如果admin用户请求/admin/foo,相拟的进程会发生。但是在认证之后,访问控制层将让请求通过:

090950434

当用户请求一个受保护资源时产生的请求流是十分简单的,但相当灵活。正如你稍后将看到的那样,认证可以通过多样方式来处理,包括通过表单登录、X.509证书或通过Twitter来认证用户。无论认证的模式如何,请求流总是相同的:

  1. 用户访问受限资源;
  2. 应用程序将用户重定向给登录表单;
  3. 用户提交它的证书(如:用户名/密码);
  4. 防火墙认证用户;
  5. 认证用户重发原始请求。

确切的过程实际取决于你所使用的认证机制。举个例子,当使用表单登录时,用户提交它的证书到一个处理表单的URL(如 /login_check),然后重定向到最初请求的URL(如 /admin/foo)。但是如果是HTTP认证,用户将直接提供它的证书到原始URL(如 /admin/foo),然后在同一请求中将页面返回给用户(即:不进行重定向)
这些特质不会引起任何问题,但记住它们是有好处的。

你也将在稍后学到在Symfony2中如何保证其它事物的安全,包括特定的控制器、对象、甚至是PHP方法。

使用传统的登录表单

在本节,security.yml文件中,继续使用硬编码定义用户,然后你需要学习怎样去创建基本的登录表单。
如果从数据库中加载用户,你需要阅读How to Load Security Users from the Database (the Entity Provider)
通过阅读这篇文章的这一节,您可以从数据库中加载用户创建一个完整的登录表单系统。

在目前为止,你已经看到如何将你的应用程序放置在防火墙下,然后根据规则限制访问某些区域。通过使用HTTP认证,你可以毫不费力地进入所有浏览器都可以提供的用户名/密码框。然而,Symfony2支持许多对话框以外的认证机制。所有这一切的细节,可参见安全配置参考
在本节中,你将让用户通过一个传统的HTML登录表单来增强这一过程。
首先,在你的防火墙下启动表单登录:

 如果你不需要自定义你的login_path或check_path的值(这里的值是缺省值),你可以缩写你的配置:

 

现在,当安全系统启动认证过程时,它将用户重定向到登录表单(默认情况下是/login)。实现这个登录表单。首先创建两个路由:一个login路由显示登录表单(如:/login),一个login_check将处理登录表单的提交(如:/login_check):

 你不需要为/login_check的URL实现控制器,因为防火墙会自动捕捉和处理这一URL上的任何表单提交。然而,你一定要有个路由(如下所示)URL,以及一个用于注销的路径(查看本页 Logging Out)。

注意,login路由匹配login_path的配置值,因为在那里安全系统将进行重定向需要登录的用户。

下一步,创建显示登录表单的控制器:

不要让这个控制器迷惑你。正如你将稍后看到的那样,当用户提交表单时,安全系统自动为你处理表单提交。如果用户提交了一个非法的用户名或密码,控制器会从安全系统中读到表单提交的错误,以便返回给用户显示。

换句话说,你的工作是显示登录表单以及可能发生的错误,但安全系统自身检查提交的用户名和密码,并对该用户进行认证。

最后,创建相应的表单:

 此登录表单目前无法防止CSRF攻击,阅读 在登录窗体使用CSRF保护如何保护您的登录表单

被送入模板的错误变量是AuthenticationException的实例。它也许包含更多关于认证失败的信息,甚至是敏感信息。所以它使用的非常广泛!

表单有着非常少的要求。首先,通过提交表单到/login_check(通过login_check路由),安全系统自动为你截取表单提交并进行表单处理。其次,安全系统预期被提交的表单项是_username和_password(这些表单项名可以被配置)。

这是这样!当你提交表单时,安全系统将自动检查用户的证书,要么认证通过用户,要么将用户重定向到登录表单以显示错误信息。

回顾整个过程:

  1. 用户尝试访问受限用户;
  2. 防火墙通过将用户重定向到登录表单(/login)并启动认证过程;
  3. 在本例中,/login页通过创建路由和控制器来渲染登录页面;
  4. 用户提交登录表单到/login_check;
  5. 安全系统拦截请求,检查用户提交的证书,如果正确的话就认证通过用户,反之则将用户送回登录页面。

默认情况下,如果提交的证书是正确的,用户将被重定向到被请求的原始页(如:/admin/foo)。如果用户本来就直接访问的login页面,它将被重定向到主页。这可以允许你去定制,例如,重定向用户到指定的URL。
关于这个的更多细节,以及如何自定义表单登录过程,请参见如何自定义你的表单登录

避免常见错误
在设置登录表单时,注意一些常见的陷阱。
1. 创建正确的路由
首先,确保你已经正确地定义了/login和/login_check路由,它们对应着login_path和check_path配置值。这里的一个错误配置可能会将你重定向到404页,而非登录页面,或者提交登录表单之后什么事情也没发生(你只是一遍又一遍地看到登录页面)。
2. 确保登录页面是不安全的
同样,也需要确保登录页面是不要求任何角色就可以查看的。例如,下面的配置,为所有的URL要求ROLE_ADMIN角色(包括/login的URL),会引起循环重定义的:

在/login的URL上删除访问控制以修复该问题:

同样,如果你的防火墙不允许匿名用户的话,你也需要创建一个特殊的防火墙来让匿名用户使用登录页:

3. 确保“/login_check”在防火墙之后

接下来,确保你check_path的URL(如:/login_check)在你登录表单的防火墙之后(在本例中,单个防火墙匹配所有URL,包含/login_check)。如果/login_check没有匹配任何防火墙,你将得到不能为路径“/login_check”找到控制器的异常。

4. 多个防火墙不能共享安全内容

如果你使用多重防火墙,并且你在针对一个防火墙进行认证,那么你将不会再自动针对其它的防火墙进行认证。不同的防火墙就象不同的安全系统。这也是为什么对于大多数应用程序而言,有一个主要的防火墙就足够了的原因。

5.路由错误页面不包含在防火墙里

 

 

未完待续中…..

 

发表评论