6. Security Namespace Configuration
6.1 Introduction
自2.0版本的Spring框架以来,命名空间已经支持配置。 它允许我们使用来自附加XML模式的元素来补充传统的Spring beans应用程序上下文语法。命名空间元素可以简单的允许配置单个bean,或者更强大的,定义一个可选的配置语法,这样更贴近问题域并且对用户隐藏背后的复杂性。一个简单的元素可以隐藏多个bean和添加到应用程序上下文的多个处理步骤。例如:从安全命名空间添加后面的元素到应用程序上下文将开始一个LDAP服务到应用程序内用于测试:
<security:ldap-server />
这比布线等效的Apache目录服务器bean要简单得多。 最常见的替代配置要求是由ldap-server元素上的属性支持的,并且用户不需要担心需要创建哪些bean以及bean属性名称。在编辑应用程序上下文文件时使用良好的XML编辑器应提供有关可用属性和元素的信息。我们建议你尝试Spring工具套件,因为它具有使用标准Spring命名空间的特殊功能。
要在应用程序上下文中开始使用安全命名空间,你需要在类路径上具有spring-security-config
jar。 然后你需要做的是将模式声明添加到应用程序上下文文件中:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd">
...
</beans>
在许多示例中,你将看到(在示例应用程序中),我们通常使用"security"作为默认命名空间,而不是"beans",这意味着我们可以省略所有安全命名空间元素的前缀,使内容 更容易阅读。 如果你的应用程序上下文分为单独的文件,并且在其中一个文件中包含大部分安全配置,则可能还需要执行此操作。 然后,你的安全应用程序上下文文件将开始这样:
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd">
...
</beans:beans>
我们将假设此语法从现在开始在本章中使用。
6.1.1 Design of the Namespace
命名空间旨在捕获框架的最常见用法,并提供简化和简洁的语法,以便在应用程序中启用它们。 该设计基于框架内的大规模依赖性,可以分为以下几个方面:
- Web / HTTP安全 - 最复杂的部分。设置过滤器和相关服务bean,用于应用框架认证机制,安全URL,呈现登录和错误页面等等。
- 业务对象(方法)安全 - 用于保护服务层的选项。
- AuthenticationManager - 处理来自框架其他部分的认证请求。
- AccessDecisionManager - 提供Web和方法安全的访问决策。默认的一个将被注册,但你也可以选择使用一个自定义的,使用正常的Spring bean语法声明。
- AuthenticationProviders - 身份验证管理器对用户进行身份验证的机制。命名空间提供了对几个标准选项的支持,也是添加使用传统语法声明的自定义bean的方法
- UserDetailsService - 与身份验证提供程序密切相关,但通常也需要其他bean。
我们将在以下部分了解如何配置这些。
6.2 Getting Started with Security Namespace Configuration
在本节中,我们将介绍如何构建命名空间配置以使用框架的一些主要功能。 让我们假设你最初想要尽快开始和运行,并向现有的Web应用程序添加身份验证支持和访问控制,并进行一些测试登录。 然后,我们将看看如何转换为对数据库或其他安全存储库进行身份验证。 在后面的部分,我们将介绍更多的高级命名空间配置选项。
6.2.1 web.xml Configuration
你需要做的第一件事是将以下过滤器声明添加到你的web.xml
文件:
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
这提供了一个到Spring Security Web基础结构的钩子。 DelegatingFilterProxy
是一个Spring Framework类,它委托给一个过滤器实现,该过滤器实现在应用程序上下文中定义为一个Spring bean。 在这种情况下,bean名为springSecurityFilterChain
,它是由命名空间创建的内部基础结构bean,用于处理Web安全。 注意,你不应该自己使用这个bean名称。 将它添加到web.xml后,就可以开始编辑应用程序上下文文件。 Web安全服务使用<http>
元素配置。
6.2.2 A Minimal Configuration
启用Web安全所需的所有功能都是
<http>
<intercept-url pattern="/**" access="hasRole('USER')" />
<form-login />
<logout />
</http>
其中说我们希望我们的应用程序中的所有URL是安全的,需要角色ROLE_USER
来访问它们,我们想使用一个带有用户名和密码的表单登录应用程序,并且我们想要一个注销URL注册,这将允许我们注销应用程序。<http>
元素是所有Web相关命名空间功能的父级。 <intercept-url>
元素定义了一个模式,使用ant路径样式语法,匹配传入请求的URL。你还可以使用正则表达式匹配作为替代(有关更多详细信息,请参阅命名空间附录)。访问属性定义与给定模式匹配的请求的访问要求。使用默认配置,这通常是逗号分隔的角色列表,其中一个用户必须允许提出请求。前缀"ROLE_"是指示应当与用户的权限进行简单比较的标记。换句话说,应该使用正常的基于角色的检查。 Spring Security中的访问控制不限于使用简单角色(因此使用前缀来区分不同类型的安全属性)。我们将在后面看到解释如何改变脚注:[访问属性中的逗号分隔值的解释取决于使用的-1-的实现。在Spring Security 3.0中,该属性也可以填充为-2-。]
你可以使用多个
<intercept-url>
元素为不同的网址集合定义不同的访问要求,但会按列出的顺序进行评估,并使用第一个匹配项。 所以你必须把最具体的比赛放在顶部。 你还可以添加方法属性来限制与特定HTTP方法(GET,POST,PUT等)的匹配。
要添加一些用户,可以直接在命名空间中定义一组测试数据:
<authentication-manager>
<authentication-provider>
<user-service>
<user name="jimi" password="jimispassword" authorities="ROLE_USER, ROLE_ADMIN" />
<user name="bob" password="bobspassword" authorities="ROLE_USER" />
</user-service>
</authentication-provider>
</authentication-manager>
如果你熟悉框架的命名空间前版本,你可能已经猜到了这里发生了什么。 <http>
元素负责创建FilterChainProxy和它使用的过滤器bean。 诸如不正确的滤波器排序的常见问题不再是问题,因为滤波器位置是预定义的。 <authentication-provider>
元素创建DaoAuthenticationProvider bean
,<user-service>
元素创建InMemoryDaoImpl。 所有认证提供者元素必须是<authentication-manager>
元素的子元素,它创建一个ProviderManager
并向其注册认证提供者。 你可以找到有关在命名空间附录中创建的bean的更多详细信息。 如果你想开始了解框架中的重要类以及如何使用它们,这是值得交叉检查的,尤其是如果你想以后定制的东西。
上面的配置定义了两个用户,他们的密码和它们在应用程序中的角色(将用于访问控制)。 还可以使用user-service
上的properties属性从标准属性文件加载用户信息。 有关文件格式的更多详细信息,请参阅内存中身份验证部分。 使用<authentication-provider>
元素意味着用户信息将被认证管理器用来处理认证请求。 你可以有多个<authentication-provider>
元素来定义不同的认证源,并依次咨询每个。 在这一点上,你应该能够启动你的应用程序,你将需要登录以继续。 尝试一下,或尝试使用项目附带的"教程"示例应用程序。
6.2.3 Form and Basic Login Options
你可能想知道当提示登录时登录表单来自哪里,因为我们没有提到任何HTML文件或JSP。 实际上,由于我们没有为登录页面显式设置URL,Spring Security会根据启用的功能自动生成一个URL,并使用处理提交的登录的URL的标准值,用户将使用的默认目标URL 在登录后发送到等等。 但是,命名空间提供了大量的支持,允许你自定义这些选项。 例如,如果你想提供自己的登录页面,可以使用:
<http>
<intercept-url pattern="/login.jsp*" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<intercept-url pattern="/**" access="ROLE_USER" />
<form-login login-page='/login.jsp'/>
</http>
还要注意,我们添加了一个额外的intercept-url
元素来说明对匿名用户的任何登录页面的请求都应该可用,并且AuthenticatedVoter
类可以获得关于如何处理值IS_AUTHENTICATED_ANONYMOUSLY
的更多细节。 否则,请求将匹配模式/**
,并且将无法访问登录页面本身! 这是一个常见的配置错误,并将导致应用程序中的无限循环。 如果你的登录页面显示为安全的,Spring Security将在日志中发出警告。 也可以通过为模式定义单独的http元素,使所有匹配特定模式的请求完全绕过安全过滤器链,如下所示:
<http pattern="/css/**" security="none"/>
<http pattern="/login.jsp*" security="none"/>
<http use-expressions="false">
<intercept-url pattern="/**" access="ROLE_USER" />
<form-login login-page='/login.jsp'/>
</http>
从Spring Security 3.1,现在可以使用多个http元素为不同的请求模式定义单独的安全过滤器链配置。 如果从http元素中省略pattern属性,则它匹配所有请求。 创建不安全模式是此语法的一个简单示例,其中模式映射到空过滤器链。 我们将在安全过滤器链的章节中更详细地讨论这个新的语法。 重要的是要意识到这些不安全的请求将完全忽略任何Spring Security Web相关的配置或其他属性,如requires-channel,所以你将无法访问当前用户的信息或在请求期间调用安全方法。 如果你仍希望应用安全过滤器链,请使用
access ='IS_AUTHENTICATED_ANONYMOUSLY'
如果要使用基本认证而不是表单登录,则将配置更改为
<http use-expressions="false">
<intercept-url pattern="/**" access="ROLE_USER" />
<http-basic />
</http>
然后,基本身份验证将优先,并且将用于在用户尝试访问受保护资源时提示登录。 如果你希望使用此配置,则表单登录仍然可用于此配置,例如通过嵌入在另一个网页中的登录表单。
Setting a Default Post-Login Destination
如果尝试访问受保护资源时未提示表单登录,则将使用default-target-url
选项。 这是用户成功登录后将访问的URL,默认为"/"。 你还可以将always-use-default-target
属性设置为"true",以便用户始终在此页面结束(无论登录是“按需”还是他们明确选择登录) 。 如果应用程序始终要求用户在“主页”页面上启动,这是非常有用的,例如:
<http pattern="/login.htm*" security="none"/>
<http use-expressions="false">
<intercept-url pattern='/**' access='ROLE_USER' />
<form-login login-page='/login.htm' default-target-url='/home.htm'
always-use-default-target='true' />
</http>
为了更好地控制目标,你可以使用authentication-success-handler-ref
属性作为default-target-url
的替代。 引用的bean应该是AuthenticationSuccessHandler
的一个实例。 你可以在Core Filters章节以及命名空间附录中找到更多信息,以及有关如何在身份验证失败时自定义流的信息。
6.2.4 Logout Handling
注销元素通过导航到特定的URL添加了注销的支持。 默认注销URL是/logout
,但是你可以使用logout-url
属性将其设置为其他值。 关于其他可用属性的更多信息可以在命名空间附录中找到。
6.2.5 Using other Authentication Providers
实际上,你将需要一个更为可扩展的用户信息源,而不是添加到应用程序上下文文件中的几个名称。 很可能你会想要存储你的用户信息在类似数据库或LDAP服务器。 LDAP命名空间配置在LDAP章节中讨论,因此我们不在这里讨论。 如果你有一个Spring Security的UserDetailsService
的自定义实现,在应用程序上下文中称为"myUserDetailsService",那么你可以使用
<authentication-manager>
<authentication-provider user-service-ref='myUserDetailsService'/>
</authentication-manager>
如果要使用数据库,那么可以使用
<authentication-manager>
<authentication-provider>
<jdbc-user-service data-source-ref="securityDataSource"/>
</authentication-provider>
</authentication-manager>
其中"securityDataSource"是应用程序上下文中的DataSource
bean的名称,指向包含标准Spring Security用户数据表的数据库。 或者,你可以配置Spring Security JdbcDaoImpl
bean,并指向使用user-service-ref
属性的bean:
<authentication-manager>
<authentication-provider user-service-ref='myUserDetailsService'/>
</authentication-manager>
<beans:bean id="myUserDetailsService"
class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">
<beans:property name="dataSource" ref="dataSource"/>
</beans:bean>
你还可以使用标准AuthenticationProvider
beans如下
<authentication-manager>
<authentication-provider ref='myAuthenticationProvider'/>
</authentication-manager>
其中myAuthenticationProvider
是应用程序上下文中实现AuthenticationProvider
的bean的名称。 你可以使用多个authentication-provider
元素,在这种情况下,将按声明它们的顺序查询提供程序。 有关如何使用命名空间配置Spring Security AuthenticationManager
的更多信息,请参见第6.6节"身份验证管理器和命名空间"。
Adding a Password Encoder
密码应始终使用为此目的设计的安全散列算法(不是像SHA或MD5这样的标准算法)进行编码。 这由<password-encoder>
元素支持。 使用bcrypt编码的密码,原始验证提供程序配置将如下所示:
<beans:bean name="bcryptEncoder"
class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>
<authentication-manager>
<authentication-provider>
<password-encoder ref="bcryptEncoder"/>
<user-service>
<user name="jimi" password="d7e6351eaa13189a5a3641bab846c8e8c69ba39f"
authorities="ROLE_USER, ROLE_ADMIN" />
<user name="bob" password="4e7421b1b8765d8f9406d87e7cc6aa784c4ab97f"
authorities="ROLE_USER" />
</user-service>
</authentication-provider>
</authentication-manager>
Bcrypt是大多数情况下的不错选择,除非你有一个旧系统,迫使你使用不同的算法。 如果你使用简单的哈希算法,或者更糟的是存储纯文本密码,那么你应该考虑迁移到更安全的选项,如bcrypt。
6.3 Advanced Web Features
6.3.1 Remember-Me Authentication
有关remember-me命名空间配置的信息,请参阅单独的Remember-Me章节。
6.3.2 Adding HTTP/HTTPS Channel Security
如果你的应用程序同时支持HTTP和HTTPS,并且你需要只能通过HTTPS访问特定的URL,则可以直接使用<intercept-url>
上的requires-channel属性来支持:
<http>
<intercept-url pattern="/secure/**" access="ROLE_USER" requires-channel="https"/>
<intercept-url pattern="/**" access="ROLE_USER" requires-channel="any"/>
...
</http>
配置了这个配置后,如果用户尝试使用HTTP访问与"/secure/**“模式匹配的任何内容,它们将首先被重定向到HTTPS URL [5]。 可用的选项为"http”,“https"或"any”。 使用值"any"表示可以使用HTTP或HTTPS。
如果你的应用程序使用非标准端口进行HTTP和/或HTTPS,你可以指定端口映射列表,如下所示:
<http>
...
<port-mappings>
<port-mapping http="9080" https="9443"/>
</port-mappings>
</http>
请注意,为了真正安全,应用程序不应该使用HTTP或在HTTP和HTTPS之间切换。 它应该以HTTPS(用户输入HTTPS URL)开始,并使用安全连接,以避免任何中间人攻击的可能性。
6.3.3 Session Management
Detecting Timeouts
你可以配置Spring Security以检测无效会话ID的提交,并将用户重定向到相应的URL。 这是通过session-management
实现的:
<http>
...
<session-management invalid-session-url="/invalidSession.htm" />
</http>
请注意,如果你使用此机制检测会话超时,则可能会错误地报告错误,如果用户注销,然后重新登录,而不关闭浏览器。 这是因为会话cookie在会话失效时不会被清除,即使用户已注销,也将重新提交。 你可以在注销时显式删除JSESSIONID cookie,例如在注销处理程序中使用以下语法:
<http>
<logout delete-cookies="JSESSIONID" />
</http>
不幸的是,这不能保证与每个servlet容器一起工作,所以你需要在你的环境中测试它
如果你正在代理后运行应用程序,你还可以通过配置代理服务器来删除会话cookie。 例如,使用Apache HTTPD的
mod_headers
,以下指令将通过在对注销请求的响应中过期来删除JSESSIONID
cookie(假设应用程序部署在路径/tutorial
):
<LocationMatch "/tutorial/logout">始终 set Set-Cookie“JSESSIONID =; Path = / tutorial; Expires = Thu,01 Jan 1970 00:00:00 GMT”</ LocationMatch>
Concurrent Session Control
如果你希望限制单个用户登录到你的应用程序的能力,Spring Security支持这种开箱即用的简单添加功能。 首先,你需要将以下侦听器添加到web.xml
文件中,以使Spring Security更新有关会话生命周期事件:
<listener>
<listener-class>
org.springframework.security.web.session.HttpSessionEventPublisher
</listener-class>
</listener>
然后将以下行添加到应用程序上下文中:
<http>
...
<session-management>
<concurrency-control max-sessions="1" />
</session-management>
</http>
这将阻止用户多次登录 - 第二次登录将导致第一次登录失效。通常,你希望防止第二次登录,在这种情况下你可以使用
<http>
...
<session-management>
<concurrency-control max-sessions="1" error-if-maximum-exceeded="true" />
</session-management>
</http>
第二次登录将被拒绝。 “被拒绝"是指如果正在使用基于表单的登录,则用户将被发送到authentication-failure-url
。如果第二认证通过另一非交互机制(例如"记住我”)发生,则将向客户端发送"未授权"(401)错误。 如果你想要使用错误页面,可以将属性session-authentication-error-url
添加到会话管理元素。 如果你使用自定义的身份验证过滤器进行基于表单的登录,则必须显式配置并发会话控制支持。 更多详细信息可以在会话管理一章中找到。
Session Fixation Attack Protection
会话固定攻击是潜在的风险,其中恶意攻击者可能通过访问站点来创建会话,然后说服另一用户登录同一会话(通过发送包含会话标识符作为参数的链接,用于 例)。 Spring Security通过创建一个新的会话或者在用户登录时以其他方式更改会话ID来自动防止此情况。如果你不需要此保护或者与其他某些要求冲突,则可以使用session-fixation-protection
属性<session-management>
,其中有四个选项
-
none
- 不要做任何事情。原始会话将保留。 -
newSession
- 创建一个新的“干净”会话,而不复制现有的会话数据(Spring Security相关的属性仍将被复制)。 -
migrateSession
- 创建新会话并将所有现有会话属性复制到新会话。这是Servlet 3.0或更早版本容器中的默认值。 -
changeSessionId
- 不要创建新会话。而应使用由Servlet容器提供的会话固定保护 (HttpServletRequest#changeSessionId()
). 此选项仅在Servlet 3.1(Java EE 7)和更高版本的容器中可用。在旧容器中指定它将导致异常。这是Servlet 3.1和更新的容器中的默认值。
当发生会话固定保护时,会导致在应用程序上下文中发布SessionFixationProtectionEvent
。 如果使用changeSessionId
,此保护还将导致通知任何javax.servlet.http.HttpSessionIdListener
,因此如果你的代码侦听这两个事件,请谨慎使用。 有关其他信息,请参阅"session management"一章。
6.3.4 OpenID Support
命名空间支持OpenID登录,而不是正常的基于表单的登录,或者除了正常的基于表单的登录,还有一个简单的更改:
<http>
<intercept-url pattern="/**" access="ROLE_USER" />
<openid-login />
</http>
然后,你应该向OpenID提供程序(例如myopenid.com)注册自己,并将用户信息添加到你的内存中<user-service>
:
<user name="http://jimi.hendrix.myopenid.com/" authorities="ROLE_USER" />
你应该能够使用myopenid.com
网站进行登录验证。 还可以通过在openid-login
元素上设置user-service-ref
属性来选择特定的UserDetailsService
bean以使用OpenID。 有关详细信息,请参阅上一节有关身份验证提供程序。 请注意,我们从上述用户配置中省略了密码属性,因为这组用户数据仅用于加载用户的权限。 将在内部生成随机密码,从而防止你在配置中的其他位置意外将此用户数据用作身份验证来源。
Attribute Exchange
支持OpenID 属性交换。例如,以下配置将尝试从OpenID提供程序检索电子邮件和全名,以供应用程序使用:
<openid-login>
<attribute-exchange>
<openid-attribute name="email" type="http://axschema.org/contact/email" required="true"/>
<openid-attribute name="name" type="http://axschema.org/namePerson"/>
</attribute-exchange>
</openid-login>
每个OpenID属性的"type"是由特定模式(在本例中为http://axschema.org/)确定的URI。 如果必须检索属性以便成功认证,则可以设置required
的属性。 支持的确切模式和属性将取决于你的OpenID提供程序。 属性值作为认证过程的一部分返回,然后可以使用以下代码访问:
OpenIDAuthenticationToken token =
(OpenIDAuthenticationToken)SecurityContextHolder.getContext().getAuthentication();
List<OpenIDAttribute> attributes = token.getAttributes();
OpenIDAttribute
包含属性类型和检索的值(或多值属性的情况下的值)。 我们将在技术概述一章中查看Spring Security核心组件,了解有关如何使用SecurityContextHolder
类的更多信息。 如果你希望使用多个身份提供程序,也支持多个attribute-exchange
配置。 你可以提供多个属性交换元素,在每个元素上使用identifier-matcher
属性。 它包含将与用户提供的OpenID标识符匹配的正则表达式。 有关示例配置,请参阅代码库中的OpenID示例应用程序,为Google,Yahoo和MyOpenID提供程序提供不同的属性列表。
6.3.5 Response Headers
有关如何自定义标头元素的其他信息,请参阅参考的第20章“安全HTTP响应标头”部分。
6.3.6 Adding in Your Own Filters
如果你之前使用过Spring Security,你会知道框架维护一个过滤器链,以便应用它的服务。 你可能希望在特定位置的堆栈中添加你自己的过滤器,或者使用当前没有命名空间配置选项(例如CAS)的Spring Security过滤器。 或者,你可能希望使用标准命名空间过滤器的自定义版本,例如由<form-login>
元素创建的UsernamePasswordAuthenticationFilter
,利用一些通过明确使用bean可用的额外配置选项。 如何使用命名空间配置,因为过滤器链不直接暴露? 使用命名空间时,始终严格执行过滤器的顺序。 当创建应用程序上下文时,过滤器bean按命名空间处理代码进行排序,标准的Spring Security过滤器在命名空间中有一个别名和一个众所周知的位置。
在以前的版本中,排序是在应用程序上下文的后处理过程中创建过滤器实例之后进行的。 在版本3.0+中,排序现在在实例化类之前在bean元数据级别完成。 这对于如何将你自己的过滤器添加到堆栈有影响,因为在解析
<http>
元素期间必须知道整个过滤器列表,因此语法在3.0中略有变化。
创建过滤器的过滤器,别名和命名空间elements/attributes如表6.1“标准过滤器别名和顺序”所示。 过滤器按过滤器链中出现的顺序列出。
Table 6.1. Standard Filter Aliases and Ordering
Alias | Filter Class | Namespace Element or Attribute |
CHANNEL_FILTER |
|
|
SECURITY_CONTEXT_FILTER |
|
|
CONCURRENT_SESSION_FILTER |
|
|
HEADERS_FILTER |
|
|
CSRF_FILTER |
|
|
LOGOUT_FILTER |
| |
| ||
X509_FILTER |
|
|
PRE_AUTH_FILTER |
| N/A |
CAS_FILTER |
| N/A |
FORM_LOGIN_FILTER |
|
|
BASIC_AUTH_FILTER |
|
|
SERVLET_API_SUPPORT_FILTER |
|
|
JAAS_API_SUPPORT_FILTER |
|
|
REMEMBER_ME_FILTER |
|
|
ANONYMOUS_FILTER |
|
|
SESSION_MANAGEMENT_FILTER |
|
|
EXCEPTION_TRANSLATION_FILTER |
|
|
FILTER_SECURITY_INTERCEPTOR |
|
|
SWITCH_USER_FILTER |
| N/A |
你可以使用custom-filter
元素和其中一个名称来指定你的过滤器应该出现的位置:
<http>
<custom-filter position="FORM_LOGIN_FILTER" ref="myFilter" />
</http>
<beans:bean id="myFilter" class="com.mycompany.MySpecialAuthenticationFilter"/>
如果希望在堆栈中的另一个过滤器之前或之后插入过滤器,也可以使用after
或before
属性。 名称"FIRST"和"LAST"可以与position
属性一起使用,表示你希望过滤器分别出现在整个堆栈之前或之后。
如果你插入的自定义过滤器可能占据与命名空间创建的其中一个标准过滤器相同的位置,那么重要的是不要错误地包括命名空间版本。 删除创建要替换其功能的过滤器的任何元素。 请注意,你不能替换使用
<http>
元素本身(SecurityContextPersistenceFilter
,ExceptionTranslationFilter
或FilterSecurityInterceptor
)创建的过滤器。 默认情况下会添加其他过滤器,但你可以禁用它们。 默认情况下会添加一个AnonymousAuthenticationFilter
,除非你禁用了会话锁定保护,一个SessionManagementFilter
也将被添加到过滤器链。
如果要替换需要认证入口点的命名空间过滤器(即认证过程是由未经认证的用户尝试访问安全资源所触发的),则还需要添加自定义入口点bean。
Setting a Custom AuthenticationEntryPoint
如果你不使用表单登录,OpenID或通过命名空间的基本认证,你可能想要使用传统的bean语法定义认证过滤器和入口点,并将它们链接到命名空间中,如我们刚才看到的。 可以使用<http>
元素上的entry-point-ref
属性设置相应的AuthenticationEntryPoint
。 CAS示例应用程序是使用带命名空间的自定义bean的一个很好的示例,包括此语法。 如果你不熟悉身份验证入口点,将在技术概述一章中讨论。
6.4 Method Security
从2.0版本开始,Spring Security已经大大提高了对你的服务层方法添加安全性的支持。 它提供对JSR-250注释安全性以及框架的原始@Secured
注释的支持。 从3.0,你也可以使用新的基于表达式的注释。 你可以将安全性应用于单个bean,使用intercept-methods
元素装饰bean声明,也可以使用AspectJ样式切入点在整个服务层上保护多个bean。
6.4.1 The global-method-security Element
此元素用于在应用程序中启用基于注释的安全性(通过设置元素上的相应属性),并将安全点切分声明分组在整个应用程序上下文中。 你应该只声明一个<global-method-security>
元素。 以下声明将支持Spring Security的@Secured
:
<global-method-security secured-annotations="enabled" />
向方法(在类或接口上)添加注释将相应地限制对该方法的访问。 Spring Security的原生注释支持为方法定义了一组属性。 这些将被传递给AccessDecisionManager
,以便做出实际的决定:
public interface BankService {
@Secured("IS_AUTHENTICATED_ANONYMOUSLY")
public Account readAccount(Long id);
@Secured("IS_AUTHENTICATED_ANONYMOUSLY")
public Account[] findAccounts();
@Secured("ROLE_TELLER")
public Account post(Account account, double amount);
}
可以使用启用对JSR-250注释的支持
<global-method-security jsr250-annotations="enabled" />
这些是基于标准的,允许应用简单的基于角色的约束,但没有Spring Security的本机注释。要使用新的基于表达式的语法,你将使用
<global-method-security pre-post-annotations="enabled" />
和等效的Java代码
public interface BankService {
@PreAuthorize("isAnonymous()")
public Account readAccount(Long id);
@PreAuthorize("isAnonymous()")
public Account[] findAccounts();
@PreAuthorize("hasAuthority('ROLE_TELLER')")
public Account post(Account account, double amount);
}
如果你需要定义简单的规则,而不是根据用户的权限列表检查角色名称,基于表达式的注释是一个不错的选择。
注释的方法将仅对被定义为Spring bean的实例(在启用方法安全性的相同应用程序上下文中)是安全的。 如果你想保护不是由Spring创建的实例(例如使用new操作符),那么你需要使用AspectJ。
你可以在同一个应用程序中启用多个类型的注释,但只有一个类型应该用于任何接口或类,否则行为将不会被明确定义。 如果找到适用于特定方法的两个注释,则只应用其中一个。
Adding Security Pointcuts using protect-pointcut
使用protect-pointcut
切换特别强大,因为它允许你只使用一个简单的声明来应用安全性到许多bean。 考虑下面的例子:
<global-method-security>
<protect-pointcut expression="execution(* com.mycompany.*Service.*(..))"
access="ROLE_USER"/>
</global-method-security>
这将保护在应用程序上下文中声明的bean的所有方法,其类位于com.mycompan
y包中,其类名以"Service"结尾。 只有具有ROLE_USER
角色的用户才能调用这些方法。 与URL匹配一样,最具体的匹配必须在切入点列表中位于第一,因为将使用第一个匹配表达式。 安全注释优先于切入点。
6.5 The Default AccessDecisionManager
本节假设你对Spring Security中的访问控制的底层架构有一些了解。 如果你不能,你可以跳过它,以后再回来,因为这一节只是真正相关的人需要做一些自定义,以使用超过简单的基于角色的安全。 当你使用命名空间配置时,会为你自动注册AccessDecisionManager
的默认实例,并将根据你在intercept-url
和protect-pointcut
声明中指定的访问属性,用于为方法调用和Web URL访问做出访问决策 (如果使用注释安全方法,则在注释中)。 默认策略是使用带有RoleVoter
和AuthenticatedVoter
的AffirmativeBased
AccessDecisionManager
。 你可以在授权章节中找到有关这些的更多信息。
6.5.1 Customizing the AccessDecisionManager
如果你需要使用更复杂的访问控制策略,那么很容易为方法和Web安全设置一个替代方案。 对于方法安全性,你可以通过将global-method-security
上的access-decision-manager-ref
属性设置为应用程序上下文中相应的AccessDecisionManager
bean的ID来实现:
<global-method-security access-decision-manager-ref="myAccessDecisionManagerBean">
...
</global-method-security>
Web安全的语法是相同的,但在http
元素:
<http access-decision-manager-ref="myAccessDecisionManagerBean">
...
</http>
6.6 The Authentication Manager and the Namespace
在Spring Security中提供认证服务的主要接口是AuthenticationManager
。 这通常是Spring Security的ProviderManager
类的一个实例,如果你以前使用过该框架,你可能已经熟悉它了。 如果没有,将在技术概述一章中稍后介绍。 使用authentication-manager
命名空间元素注册bean实例。 如果你通过命名空间使用HTTP或方法安全性,则不能使用自定义AuthenticationManager
,但这不应该是一个问题,因为你可以完全控制所使用的AuthenticationProvider
。 你可能想要使用ProviderManager
注册其他AuthenticationProvider
bean,你可以使用具有ref
属性的<authentication-provider>
元素来执行此操作,其中属性的值是要添加的提供程序bean的名称。 例如:
<authentication-manager>
<authentication-provider ref="casAuthenticationProvider"/>
</authentication-manager>
<bean id="casAuthenticationProvider"
class="org.springframework.security.cas.authentication.CasAuthenticationProvider">
...
</bean>
另一个常见的要求是上下文中的另一个bean可能需要引用AuthenticationManager
。你可以轻松地注册AuthenticationManager
的别名,并在应用程序上下文中的其他位置使用此名称。
<security:authentication-manager alias="authenticationManager">
...
</security:authentication-manager>
<bean id="customizedFormLoginFilter"
class="com.somecompany.security.web.CustomFormLoginFilter">
<property name="authenticationManager" ref="authenticationManager"/>
...
</bean>