6.1 Introduction(简介)
命名空间配置在Spring框架的2.0版本就可以使用了,他允许你通过额外的XML架构元素补充传统的Spring bean应用程序上下文。你可以从Spring的参考文档找到更多信息 Reference Documentation.。命名空间元素可以简单的允许配置单个bean,或者更强大的,定义一个可选的配置语法,这样更贴近问题域并且对用户隐藏背后的复杂性。一个简单的元素可以隐藏多个bean和添加到应用程序上下文的多个处理步骤。例如:从安全命名空间添加后面的元素到应用程序上下文将开始一个LDAP服务到应用程序内用于测试:
这比配置一个Apache木服务器bean简单的多。最常见的替代配置需求是ldap-server元素的属性支持,用户不用担心他们要创建的bean属名称:[你可以从:specialcharacters,macros[LDAP 身份验证]找到如何使用ldap-server元素的更多的信息. ] 。当编译应用的Context文件时良好的XML编译器应该可以提供您可用的属性和元素的信息。我们建议你尝试使用 Spring Tool Suite,它具有与标准Spring命名空间工作的特别的功能。
为了在你的应用程序上下文中使用安全命名空间,你需要将spring-security-config jar包放到你的classpath中。然后在你的上下文文件中加入以下的结构声明:
在很多示例(包括示例应用程序)中你将会看到,我们经常使用security作为默认的命名空间而不是使用beans,这样我们可以在所有安全命名空间中忽略前缀,使得内容更加容易阅读。如果你的应用程序上下文被分割成单独的文件,大部分的安全配置被放到一个文件中,你可能也想这样做。你的安全应用上下文文件应该像下面的一样:
我们假设在这一章节我们都使用这种语法。
6.1.1 Design of the Namespace(命名空间的设计)
命名空间旨在捕获框架的最常见用途,并提供简化和简洁的语法,以便在应用程序中启用它们。该设计基于框架内的大规模依赖关系,可分为以下几个方面:
Web/HTTP Security——最复杂的部分。设置过滤器和相关的服务beans,用于应用框架认证机制、保护网址、呈现登录和错误页面等等。
Business Object (Method) Security——用于保护服务层的选项。
AuthenticationManager -处理来自框架其他部分的身份验证请求。
AccessDecisionManager -为web和方法安全性提供访问决策。将注册一个默认的,但是您也可以选择使用一个自定义的,使用普通的Spring bean语法声明。
AuthenticationProviders -身份验证管理器对用户进行身份验证的机制。命名空间提供了对几个标准选项的支持,也提供了一种添加使用传统语法声明的自定义beans的方法。
UserDetailsService -与身份验证提供者密切相关,但通常也是其他beans所需要的。
我们将在后续章节查看怎么配置他们。
6.2 Getting Started with Security Namespace Configuration(开始使用安全命名空间配置)
在本节中,我们将了解如何构建一个命名空间配置来使用框架的一些主要功能。让我们假设您最初希望尽可能快地启动和运行,并通过几次测试登录向现有的web应用程序添加身份验证支持和访问控制。然后,我们将看看如何转换到针对数据库或其他安全存储库的身份验证。在后面的章节中,我们将介绍更高级的命名空间配置选项。
6.2.1 web.xml Configuration(web.xml配合)
你需要做的第一件事情是添加下面的过滤器定义到你的web.xml文件:
这为Spring安全网络基础设施提供了一个钩子。DelegatingFilterProxy是一个Spring框架类,它委托给一个过滤器实现,该过滤器实现在应用程序上下文中被定义为Spring bean。在这种情况下,该bean被命名为“springSecurityFilterChain”,这是一个由命名空间创建的内部基础结构bean,用于处理web安全性。请注意,您不应该自己使用这个bean名称。一旦将它添加到web.xml中,就可以开始编辑应用程序上下文文件了。Web安全服务是使用< http >元素配置的。
6.2.2 A Minimal Configuration(最小配置)
开始开启网页安全你只需要:
这表示我们希望应用程序中的所有网址都受到保护,要求角色ROLE_USER访问它们,我们希望使用带有用户名和密码的表单登录到应用程序,并且我们希望注册一个注销网址,这将允许我们注销应用程序。< http >元素是所有与web相关的命名空间功能的父元素。
你可以使用多个
了添加一些用户,你可以直接在命名空间直接定义一组测试数据。
如果你熟悉框架的预命名空间版本,你很可能已经猜到这里怎么回事了。
上面的配置定义了两个用户、他们的密码以及他们在应用程序中的角色(将用于访问控制)。还可以使用user-service(用户服务)的properties属性从标准属性文件中加载用户信息。有关文件格式的更多详细信息,请参见内存中身份验证( in-memory authentication )部分。使用
此时,您应该能够启动应用程序,并且需要登录才能继续。尝试一下,或者尝试一下项目附带的“教程”示例应用程序。
6.2.3 Form and Basic Login Options(表单和基本登录选项)
当提示您登录时,您可能想知道登录表单是从哪里来的,因为我们没有提到任何HTML文件或JSP。事实上,由于我们没有为登录页面明确设置一个网址,Spring Security会自动生成一个网址,基于已启用的功能,并使用处理已提交登录的网址的标准值、用户登录后将被发送到的默认目标网址等等。但是,命名空间提供了大量支持,允许您自定义这些选项。例如,如果您想提供自己的登录页面,您可以使用:
还要注意,我们已经添加了一个额外的拦截url元素,表示任何对登录页面的请求都应该对匿名用户可用。有关如何处理值IS _ AUTHENTICATED _ ANONYMOUSLY的更多详细信息,请参见AuthenticatedVoter 表决器类。否则,请求将与模式/**匹配,并且无法访问登录页面本身!这是一个常见的配置错误,将在应用程序中导致无限循环。如果您的登录页面看起来是安全的,Spring Security将在日志中发出警告。通过为模式定义一个单独的http元素,还可以让所有匹配特定模式的请求完全绕过安全过滤器链,如下所示:
从Spring Security 3.1开始,现在可以使用多个http元素为不同的请求模式定义单独的安全过滤器链配置。如果http元素中省略了模式属性,它将匹配所有请求。创建一个不安全的模式是这种语法的一个简单例子,其中模式被映射到一个空的过滤器链。我们将在关于安全过滤器链的章节中更详细地了解这种新语法。
重要的是要认识到,这些不安全的请求将完全忽略任何与Spring Security web相关的配置或其他属性,如requires-channel,因此,在请求期间,您将无法访问当前用户的信息或调用安全方法。如果您仍然希望应用安全筛选器链,请使用访问= ' IS _ AUTHORDINATIC _ ANTHORY '作为替代。
如果要使用基本身份验证而不是表单登录,请将配置更改为
基本身份验证将优先,并用于在用户试图访问受保护资源时提示登录。如果您希望使用表单登录,表单登录在此配置中仍然可用,例如,通过嵌入在另一个网页中的登录表单。
Setting a Default Post-Login Destination(设置默认的登录后目标)
如果试图访问受保护的资源时没有提示表单登录,则默认目标url(default-target-url)选项会起作用。这是用户成功登录后将访问的网址,默认为“/”。您还可以通过将“始终使用默认目标”(always-use-default-target)属性设置为“true”来配置用户,以便用户总是在此页面结束(无论登录是“按需”还是他们明确选择登录)。如果您的应用程序总是要求用户从“主页”开始,这很有用,例如:
为了更好地控制目的地,您可以使用authentication-success-handler-ref属性作为default-target-url的替代。引用的bean应该是AuthenticationSuccessHandler的实例。您将在Core Filters (核心过滤器)一章和命名空间附录中找到更多这方面的内容,以及如何在身份验证失败时自定义流的信息。
6.2.4 Logout Handling
logout元素通过导航到特定的网址来增加对注销的支持。默认的注销网址是/logout,但是您可以使用logout-url属性将其设置为其他内容。有关其他可用属性的更多信息,请参见命名空间附录。
6.2.5 Using other Authentication Providers(使用其他身份验证提供程序)
实际上,您需要一个比添加到应用程序上下文文件中的几个名字更具可伸缩性的用户信息源。很可能您会希望将用户信息存储在数据库或LDAP服务器之类的东西中。LDAP命名空间配置将在LDAP一章中讨论,因此我们在这里不再赘述。如果您在应用程序上下文中有一个自定义的Spring安全的UserDetailsService实现,称为“myUserDetailsService”,那么您可以使用
如果您想使用数据库,那么您可以使用
其中“securityDataSource”是应用程序上下文中数据源bean的名称,指向包含标准的Spring Security用户数据表的数据库(user data tables)。或者,您可以配置一个Spring Security JdbcDaoImpl bean,并使用user-service-ref引用属性指向它:
您还可以使用标准的身份验证提供程序beans,如下所示
其中myAuthenticationProvider是应用程序上下文中实现AuthenticationProvider的bean的名称。您可以使用多个身份验证提供程序元素,在这种情况下,将按照声明的顺序查询提供程序。有关如何使用命名空间配置Spring Security AuthenticationManager的更多信息,请参见第6.6节“身份验证管理器和命名空间”( Section 6.6, “The Authentication Manager and the Namespace” )。
Adding a Password Encoder
密码应始终使用专门设计的安全哈希算法进行编码(不是像SHA或MD5这样的标准算法)。这是由
bcrypt在大多数情况下都是一个不错的选择,除非您有一个遗留系统迫使您使用不同的算法。如果您使用简单的哈希算法,或者更糟糕的是,存储纯文本密码,那么您应该考虑迁移到更安全的选项,如bcrypt。
6.3 Advanced Web Features(高级web功能)
6.3.1 Remember-Me Authentication(记忆认证)
有关remember-me的命名空间配置的信息,请参见单独的 Remember-Me chapter 。
6.3.2 Adding HTTP/HTTPS Channel Security(增加HTTP/HTTPS通道安全性)
如果您的应用程序同时支持HTTP和HTTPS,并且您要求特定的url只能通过HTTPS访问,则可以使用
有了这种配置,如果用户试图使用HTTP访问任何与“/secure/**”模式匹配的内容,他们将首先被重定向到HTTPS URL [5]。可用选项有“http”、“https”或“any”。使用值“any”意味着可以使用HTTP或HTTPS。
如果您的应用程序对HTTP和/或HTTPS使用非标准端口,您可以按如下方式指定端口映射列表:
请注意,为了真正安全,应用程序根本不应该使用HTTP或者在HTTP和HTTPS之间切换。它应该从HTTPS开始(用户输入一个HTTPS网址),并始终使用安全连接,以避免任何中间人攻击的可能性。
你可以添加自己的过滤器到列表中,使用custom-filter过滤元件和这些名字来指定你的过滤器应该出现在的位置之一:
你也可以使用after和before属性来让你的过滤器插入到列表中的其他过滤器的前面和后面。FIRST和LAST可以用在position属性来设置你希望将你的过滤器插入到整个列表的前面或者后面。
如果您要插入一个自定义过滤器,该过滤器可能与命名空间创建的标准过滤器占据相同的位置,那么不要错误地包含命名空间版本是很重要的。删除任何创建过滤器的元素,这些过滤器的功能将被您替换。请注意,您不能替换使用< http >元素本身创建的筛选器-SecurityContextPersistenceFilter、ExceptionTranslationFilter或FilterSecurityInterceptor。一些其他的过滤器是默认被添加的,但是你可以禁止他们。除非你禁用session-fixation protection ,一个AnonymousAuthenticationFilter 会默认被添加,一个SessionManagementFilter也会被添加到过滤器链。
如果你替换的命名空间过滤器需要一个验证入口点(例如:在认证过程是通过尝试触发未认证用户访问以受保护资源)你也需要添加一个定制的入口点Bean。
Setting a Custom AuthenticationEntryPoint(设置自定义身份验证输入点)
如果你没有使用表单登录,OpenID或基本验证,你可能想像我们之前看到的一样,使用传统的bean语法,定义一个验证过滤器和入口点链接到命名空间。相应的AuthenticationEntryPoint可以使用
CAS示例应用是一个很好的示例,展示命名空间的定制bean的使用。如果你不熟悉认证的入口点,可以在technical overview章节看到相关讨论。
6.3.3 Session Management(会话管理)
Detecting Timeouts(超时检测)
您可以配置Spring Security来检测无效会话id的提交,并将用户重定向到适当的URL。这是通过session-management元素实现的:
请注意,如果您使用这种机制来检测会话超时,如果用户注销然后不关闭浏览器重新登录,它可能会错误地报告错误。这是因为当您使会话无效时,会话cookie不会被清除,即使用户已注销,会话cookie也会被重新提交。您可以在注销时显式删除JSESSIONID cookie,例如在注销处理程序中使用以下语法:
不幸的是,这不能保证与每个servlet容器一起工作,所以您需要在您的环境中测试它
如果您在代理后面运行应用程序,您也可以通过配置代理服务器来删除会话cookie。例如,使用Apache HTTPD的mod_headers,下面的指令将通过在响应注销请求时使JSESSIONID cookie过期来删除它(假设应用程序是在路径/tutorial下部署的):
Concurrent Session Control(并发会话控制)
如果您希望限制单个用户登录您的应用程序的能力,Spring Security会通过以下简单的添加来支持这一点。首先,您需要将以下侦听器添加到您的web.xml文件中,以保持Spring Security关于会话生命周期事件的更新:
然后将以下行添加到您的应用程序上下文中:
这将防止用户多次登录——第二次登录将导致第一次登录无效。通常情况下,您更希望阻止第二次登录,在这种情况下,您可以使用
第二次登录将被拒绝。“拒绝”是指如果使用基于表单的登录,用户将被发送到authentication-failure-url。如果第二次认证是通过另一种非交互机制进行的,例如“记住我”,则“未授权的”(401)错误将被发送到客户端。如果您想使用错误页面,可以将属性session-authentication-error-url添加到session-management元素中。
如果您对基于表单的登录使用自定义的身份验证过滤器,那么您必须显式配置并发会话控制支持。更多详细信息可在会话管理一章( Session Management chapter)中找到。
Session Fixation Attack Protection(会话固定攻击保护)
会话固定攻击是一种潜在的风险,恶意攻击者可能通过访问站点来创建会话,然后说服另一个用户使用相同的会话登录(例如,通过向他们发送包含会话标识符作为参数的链接)。Spring Security通过创建一个新的会话或在用户登录时更改会话ID来自动防止这种情况。如果您不需要这种保护,或者它与其他一些要求冲突,您可以使用
none - 什么都不做,原来的会话将会保留
newSession - 创建一个新的干净的Session,不会复制已经存在的Session属性到新的Session,这是Servlet3.0及之前的容器的默认设置
migrateSession - 创建一个新的Session并且拷贝所有已经存在的Session属性到新的Session,这是Servlet3.0及之前的容器的默认设置。
changeSessionId - 不创建新的Session,使用Servlet容器提供的Session完成攻击保护(HttpServletRequest#changeSessionId())。这个选项只有在Servlet3.1(java EE 7)和更新的容器下可用。在旧的容器设置这个选项会产生一个异常。在Servlet3.0和更新的容器就默认该选项。
当会话完成保护发生时,它会产生SessionFixationProtectionEvent发布到应用程序上下文,如果使用changeSessionId, 这种保护也将导致 任何javax.servlet.http.HttpSessionIdListener被通知,所以如果你的代码监听这两个事件要特别小心。查看Session Management 管理章节查看更多信息。
6.3.4 OpenID Support(openID支持)
该命名空间支持 OpenID登录,替代或补充了普通的基于表单的登录,只需简单的更改:
然后,您应该向OpenID提供商注册,并将
你应该能够使用myopenid.com网站登录来进行验证。另外,也可以通过设置 openid-login元素的user-service-ref属性来指定一个UserDetailsService bean来使用OpenID,有关更多信息,请参见前面关于authentication providers 提供程序的部分。请注意,我们已经从上面的用户配置中省略了密码属性,因为这组用户数据仅用于为用户加载权限。将在内部生成一个随机密码,防止您在配置的其他地方意外地将此用户数据用作身份验证源。
Attribute Exchange(属性改变)
支持OpenID属性交换(attribute exchange)。例如,以下配置将尝试从OpenID提供程序检索电子邮件和全名,供应用程序使用:
每个OpenID属性的“type”是一个URI,有一种特定的模式来确定,这个例子中是 http://axschema.org/。如果属性必须在成功认证后接收,可以设置required 属性。确切的模式和属性的支持将取决于你的OpenID提供商。该属性值返回作为认证过程的一部分,随后可以使用下面的代码访问:
OpenIDAttribute包含了属性类型和接收到的值(在有多个属性值的情况下包含多个值),通过查看Spring Security核心组件的technical overview章节我们可以了解更多的SecurityContextHolder类的使用方法。如果您希望使用多个身份提供者,也支持多属性交换配置。您可以提供多个属性交换元素,每个元素使用一个标识符匹配器属性。它包含一个正则表达式,会匹配由用户提供的OpenID标识符。查看代码库的OpenID示例应用的一个示例配置。对 Google, Yahoo 和MyOpenID 提供了不同的属性列表。
6.3.5 Response Headers
有关如何自定义标头元素的更多信息,请参考第20章“安全HTTP响应标头”一节( Chapter 20, Security HTTP Response Headers )。
6.3.6 Adding in Your Own Filters(添加您自己的过滤器)
如果你以前使用过Spring Security,你就会知道,这个 框架维护一个过滤器链,以便应用它的服务。你可能想要添加自己的过滤器到过滤器堆栈的特定位置,或者使用一个Spring Security还没有一个命名空间配置的选项的过滤器(比如CAS)。或者你想使用一个标准命名空间过滤器的定制化版本,比如UsernamePasswordAuthenticationFilter是由
使用命名空间时过滤器的顺序始终严格执行,当创建应用程序上下文,过滤器Bean被命名空间处理代码进行排序,标准的Spring Security过滤器都具有的命名空间和一个众所周知的位置的别名。
在以前的版本中,排序发生在创建过滤器实例之后,在应用程序上下文的后处理过程中。在3.0+版本中,在类被实例化之前,排序现在在bean元数据级别完成。这对如何将您自己的过滤器添加到堆栈有影响,因为在解析< http >元素的过程中,必须知道整个过滤器列表,所以在3.0中语法略有变化。
创建过滤器的过滤器、别名和命名空间元素/属性如表6.1“标准过滤器别名和排序”(Table 6.1, “Standard Filter Aliases and Ordering”)所示。过滤器按它们在过滤器链中出现的顺序列出。
6.4 Method Security(方法安全)
6.4.1 The Element
此元素用于在应用程序中启用基于注释的安全性(通过在元素上设置适当的属性),还用于将安全切入点声明组合在一起,这些声明将在整个应用程序上下文中应用。您应该只声明一个
向方法(在类或接口上)添加注释会相应地限制对该方法的访问。Spring Security的本机注释支持为该方法定义了一组属性。这些将被传递给访问决策管理器(AccessDecisionManager),以便它做出实际的决策:
可以使用启用对JSR-250注释的支持
这些都是基于标准的,允许应用简单的基于角色的约束,但不具备Spring Security的原生注释。要使用新的基于表达式的语法,您应该使用
等效的Java代码是
如果您需要定义简单的规则,而不仅仅是根据用户的权限列表检查角色名称,那么基于表达式的注释是一个不错的选择。
被注解的方法将仅在被定义为Spring 的Bean的实例时才能确保安全(在相同的应用程序的上下文中该方法-启用方法安全检查)。如果你想确保非Sprign创建的实例的安全性(比如使用new操作符创建的)那么你需要使用 AspectJ.
你可以在同一个应用程序中启用多种注解,但是在一个接口或者类中只能使用一种类型的注解,否则会出现不明确的行为。如果对特定的方法使用了两个注解,只有其中的一个会被应用。
Adding Security Pointcuts using protect-pointcut(使用保护切入点添加安全切入点)
保护切入点的使用特别强大,因为它允许您只需要简单的声明就可以对许多beans应用安全性。考虑以下示例:
这将保护在应用程序上下文定义的所有在com.mycompany包下类名以“service”结尾的类的方法。只有拥有ROLE_USER角色用户才能执行这些方法。和URL匹配一样,列表中多个匹配的话将会使用第一个匹配的安全注解,比切入点有更高的优先级。
6.5 The Default AccessDecisionManager(默认访问决策管理器)
本节假设您对Spring Security中的访问控制的底层架构有所了解。如果没有,您可以跳过它,稍后再来,因为这一部分只与那些需要进行一些定制以使用不仅仅是简单的基于角色的安全性的人真正相关。
当你使用命名空间配置时,一个AccessDecisionManager实例将会自动创建并注册用来按照你在intercept-url和protect-pointcut(还有如果你使用了方法注解安全也包含在内)定义的访问属性进行访问决策。
默认的策略是使用一个AffirmativeBased、AccessDecisionManager和RoleVoter``AuthenticatedVoter,你也可以从authorization章节找到更多信息.
6.5.1Customizing the AccessDecisionManager 定制访问决策管理器
如果你需要使用一个更复杂的访问控制策略,那么很容易的为方法和Web安全设置替代方案。
对于方法安全,通过在access-decision-manager-ref上设置global-method-security 属性来为应用程序指导适当的 AccessDecisionManager的Bean ID。
web安全的语法是相同的,但是在http元素上:
在Spring Security中提供认证服务的主要接口是AuthenticationManager。这通常是Spring Security的ProviderManager类的一个实例,如果您以前使用过该框架,您可能已经熟悉它了。如果没有,将在后面的技术概述章节( technical overview chapter)中介绍。bean实例是使用身份验证管理器(authentication-manager)命名空间元素注册的。如果通过命名空间使用的是HTTP或方法安全性,则不能使用自定义身份验证管理器,但这应该不是问题,因为您可以完全控制所使用的身份验证提供程序。
你可能需要使用ProviderManager注册其他的AuthenticationProvider Bean,你可以使用
另一个常见的要求是上下文中的另一个bean可能需要对AuthenticationManager的引用。你可以注册一个AuthenticationManager的别名在应用程序上下文中的其他地方使用这个名字。
1、您可以在第29章“ldap身份验证”中找到有关LDAP服务器元素的更多信息。
2、有关如何实际执行匹配的更多详细信息,请参见“Web应用程序基础结构”一章中第13.4节“请求匹配和HttpFirewall”。
3、参见第22章匿名身份验证一章
4、使用多个< http >元素是一个重要的特性,例如,允许命名空间在同一个应用程序中同时支持有状态和无状态路径。在拦截url元素上使用属性filters="none "的先前语法与此更改不兼容,并且在3.1中不再受支持。
5、有关如何实现通道处理的更多细节,请参见通道处理过滤器和相关类的Javadoc。