三步之内使我们的应用变得安全
尽管 Spring Security 的配置可能会很难,但是它的作者是相当为我们着想的,因为他们为我们提供了一种简单的机制来使用它很多的功能并可以此作为起点。以这个为起点,额外的配置能够实现应用的分层次详细的安全控制。
我们将从我们不安全的在线商店开始,并且使用三步操作将它变成一个拥有基本用户名和密码安全认证的站点。这个认证仅仅是为了阐述使用 Spring Security 使我们应用变得安全的步骤,所以你可能会发现这样的方式会有明显不足,这将会引领我们在以后的配置中不断进行改良。
实现 Spring Security 的 XML 配置文件
起点配置的第一步是创建一个 XML 的配置文件,用来描述所有需要的 Spring Security 组件,这些组件将会控制标准的 web 请求。
在 WEB-INFO 目录下建立一个名为 dogstore-security.xml 的 XML 文件。文件的内容如下所示:
- <? xml version = "1.0" encoding = "UTF-8" ?>
- < beans:beans xmlns = "http://www.springframework.org/schema/security"
- xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
- xmlns:beans = "http://www.springframework.org/schema/beans"
- xsi:schemaLocation ="
- http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans.xsd
- http://www.springframework.org/schema/security
- http://www.springframework.org/schema/security/
- spring-security-3.0.xsd">
- < http auto-config = "true" >
- < intercept-url pattern = "/*" access = "ROLE_USER" />
- </ http >
- < authentication-manager alias = "authenticationManager" >
- < authentication-provider >
- < user-service >
- < user authorities = "ROLE_USER" name = "guest" password = "guest" />
- </ user-service >
- </ authentication-provider >
- </ authentication-manager >
- </ beans:beans >
这是我们应用中获得最小安全配置的唯一一个 Spring Security 配置文件。这个配置文件的格式使用了 Spring Security 特定的 XML 语法,一般称之为 security 命名空间。它在 XML 的命名空间中声明( http://www.springframework.org/schema/security )并与 XML 配置元素关联。我们将在在第六章:高级配置与扩展 中讨论一种替代的配置方式,即使用传统的 Spring Bean 设置方式。
【讨厌 Spring XML 配置的用户可能会失望了,因为 Spring Security 没有像 Spring 那样提供可替代的注解机制。仔细想一下也是可以理解的,因为 Spring Security 关注的是整个系统而不是单个对象或类。但未来,我们可能能够在 Spring MVC 的控制器上使用安全注解,而不是在一个配置文件中指明 URL 模式!】
尽管在 Spring Security 中注解并不普遍,但正如你所预料的那样,对类或方法进行的配置是可以通过注解来完成的。我们将在第五章:精确的访问控制 中介绍。
添加 Spring DelegatingFilterProxy 到 web.xml 文件
Spring Security 对我们应用的影响是通过一系列的 ServletRequest 过滤器实现的(我们将会在介绍 Spring Security 架构的时候进行阐述)。可以把这些过滤器想成位于每个到我们应用的请求周围的具有安全功能的三明治。(这个比喻够新鲜,不过 Spring Security 的核心确实就是一系列介于真正请求之间的过滤器,译者注)。
Spring Security 使用了 o.s.web.filter.DelegatingFilterProxy 这个 servlet 过滤器来包装所有的应用请求,从而保证它们是安全的。
【 DelegatingFilterProxy 实际上是 Spring 框架提供的,而不是安全特有的。这个过滤器一般在 Spring 构建的 web 应用工程中使用,并将依赖于 servlet 过滤器的 Spring Bean 与 Servle 过滤器的生命周期结合起来。】
通过在 web.xml 部署描述文件中添加如下的代码,就可以配置这样一个过滤器。这段代码位于 Spring MVC 的 <servlet-mapping> 之后:
- < filter >
- < filter-name > springSecurityFilterChain </ filter-name >
- < filterclass >
- org.springframework.web.filter.DelegatingFilterProxy
- </ filter-class >
- </ filter >
- < filter-mapping >
- < filter-name > springSecurityFilterChain </ filter-name >
- < url-pattern > /* </ url-pattern >
- </ filter-mapping >
我们所做的是使用一个 ServletRequest 过滤器并将它配置成处理匹配给定 URL 模式( /* )的请求。因为我们配置的这个通配符模式匹配所有的 URL ,所以这个过滤器将会应用于每个请求。
如果你有心的话,可能会发现这与我们的 Spring Security 配置文件即 dogstore-security.xml 没有任何的关系。为了实现这个,我们需要添加一个 XML 配置文件的应用到 web 应用的部署描述文件 web.xml 中。
添加 Spring Security XML 配置文件的应用到 web.xml
取决于你如何配置你的 Spring web 应用,不知你是否已经在 web.xml 中有了对 XML 配置文件的明确引用。 Spring web 的 ContextLoaderListener 的默认行为是寻找与你的 Spring web servlet 同名的 XML 配置文件。让我们看一下如何添加这个新的 Spring Security XML 配置文件到已经存在的 JBCP Pet 商店站点中。
首先,我们需要看一下这个应用是否使用了 Spring MVC 的自动查找 XML 配置文件的功能。我们看一下在 web.xml 中 servlet 的名字,以 <servlet-name> 原始进行标识:
- < servlet >
- < servlet-name > dogstore </ servlet-name >
- < servletclass >
- org.springframework.web.servlet.DispatcherServlet
- </ servlet-class >
- < load-on-startup > 1 </ load-on-startup >
- </ servlet >
Servlet 的名字是( <servlet-name> )是 dogstore ,所以 Spring 的约定胜于配置( Convention
over Configuration , CoC )将会在 WEB-INF 目录下寻找名为 dogstore-servelt.xml 的配置文件。我们没有覆盖这种默认行为,你能在 WEB-INF 目录下找到这个文件,它包含了一些 Spring MVC 相关的配置。
【很多 Spring Web Flow 和 Spring MVC 的用户并不明白这些 CoC 规则是如何生效的以及 Spring 的代码中在何处声明了这些规则。 o.s.web.context.support.XmlWebApplicationContext 和它的父类是了解这些的一个很好的起点。 JavaDoc 在讲解基于 Spring MVC 框架的 web 工程的一些参数配置方面也做得不错。】
也可以声明额外的 Spring ApplicationContext 配置文件,它将会先于 Spring MVC servle 关联的配置文件加载。这通过 Spring 的 o.s.web.context.ContextLoaderListener 创建一个独立于 Spring MVC ApplicationContext 的另一个 ApplicationContext 来实现。这是通常的非 Spring MVC beans 配置的方式,也为 Spring Security 相关的配置提供了一个好地方。
在 web 应用的部署描述文件中,用来配置 ContextLoaderListener 的 XML 文件地址是在 <context-param> 元素中给出的:
- < context-param >
- < param-name > contextConfigLocation </ param-name >
- < param-value >
- /WEB-INF/dogstore-base.xml
- </ param-value >
- </ context-param >
dogstore-base.xml 文件中包含了一些标准的 Spring bean 的配置,如数据源、服务层 bean 等。现在,我们能够添加一个包含 Spring Security 的 XML 配置文件了,下面是更新后的 <context-param> 元素:
- < context-param >
- < param-name > contextConfigLocation </ param-name >
- < param-value >
- /WEB-INF/dogstore-security.xml
- /WEB-INF/dogstore-base.xml
- </ param-value >
- </ context-param >
在我们添加完新的 Spring Security 配置文件到 web 部署文件并重启 web 工程后,尝试访问应用的首页地址: http://localhost:8080/JBCPPets/home.do ,你将会看到如下的界面:
漂亮!我们已经使用 Spring Security 在三步之内实现了一个简单的安全层。在这里,你可以使用 guest 作为用户名和密码进行登录。接着你将能够看到 JBCP Pets 应用的一个简单首页。
为了简便起见,本章的源码只包含了全部 JBCP Pets 整个应用的很小一部分(只有一个页面)。我们这么做是为了更简洁,同时也能让构建和部署应用时不必考虑我们将在后续章节中涉及到的附加功能。这也提供了一个很好的起点让你快速的体验参数的配置并重新部署以查看它们是否生效。
注意这些不足之处
到此为止,思考一下我们所做的事情。你可能已经意识到在产品化之前应用存在很多很明显的问题,这需要很多后续的工作和对 Spring Security 产品的了解。尝试列举一下在将这个实现安全功能的站点上线前还需要什么样的完善。
实现 Spring Security 起点级别的配置是相当迅速的,它已经提供了一个登录界面,用户名密码认证以及自动拦截我们在线商店的 URL 。但是在自动配置给我们提供的功能与我们的最终目标之间有很大的差距:
<!--[if !supportLists]-->l <!--[endif]-->我们将用户名、密码以及角色信息硬编码到 XML 配置文件中。你是否还记得我们添加的这部分 XML 内容:
- < authentication-manager alias = "authenticationManager" >
- < authentication-provider >
- < user-service >
- < user authorities = "ROLE_USER" name = "guest" password = "guest" />
- </ user-service >
- </ authentication-provider >
- </ authentication-manager >
你可以看到用户名和密码在这个文件中。你不可能愿意为每一个系统的用户都在这个 XML 文件中添加一个声明。要解决这个问题,你需要使用一个基于数据库的认证提供者( authentication provider )来替代它(我们将第四章:凭证安全存储 完成它)。
<!--[if !supportLists]-->l <!--[endif]-->我们对所有的页面都进行了安全控制,包括一些潜在客户可能想匿名访问的界面。我们需要完善系统的角色以适应匿名的、认证过的以及管理权限的用户(这也将在第四章中讨论)。
<!--[if !supportLists]-->l <!--[endif]-->登录界面非常应用,但是它太基础了而且与我们 JBCP 商店风格一点也不一致。我们需要添加一个登录的 form 界面,并与我们应用的外观和风格一致(我们将在下一章解决这个问题)。
常见问题
很多用户在初次使用 Spring Security 时会遇到一些问题。我们列出了几个常见的问题和建议。我们希望你能够一直跟随着本书的讲解,运行我们示例代码。
<!--[if !supportLists]-->l <!--[endif]-->确保你的应用在添加 Spring Security 之前是可以编译和部署的。必要的时候看一些你所使用的 servlet 容器的入门级例子和文档。
<!--[if !supportLists]-->l <!--[endif]-->通常使用一个 IDE 如 Eclipse 会极大地简化你使用的 servlet 容器。不仅能够保证部署准确无误,它所提供的控制台日志也很易读可用来检查错误。你还可以在关键的位置添加断点,运行的时候会触发从而简化分析错误的过程。
<!--[if !supportLists]-->l <!--[endif]-->如果你的 XML 配置文件不正确,你会得到这样的提示信息(或类似的): org.xml.sax.SAXParseException: cvc-elt.1: Cannot find the declaration of element 'beans' 。为实现 Spring Security 的正确配置需要各种各样的 XML 命名空间引用,这可能会使很多用户感到迷惑。重新检查一下这个例子,仔细查看一下 schame 声明的部分,并使用 XML 校验器来保证你没有不合法的 XML 。
<!--[if !supportLists]-->l <!--[endif]-->确保你所使用的 Spring 和 Spring Security 版本匹配,并确保没有你的应用中不存在没用的 Spring jar 包。