# 安全名称空间配置

## 导言

自 Spring 框架的2.0版本以来，名称空间配置一直可用。它允许你使用额外的XML模式中的元素来补充传统的 Spring bean应用程序上下文语法。你可以在 Spring [参考文献](https://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/)中找到更多信息。名称空间元素可以简单地用于允许以更简洁的方式配置单个 Bean，或者更强大地，用于定义替代配置语法，该语法更紧密地匹配问题域，并向用户隐藏潜在的复杂性。一个简单的元素可能掩盖了一个事实，即多个bean和处理步骤正在被添加到应用程序上下文中。例如，将以下元素从安全名称空间添加到应用程序上下文中，将启动一个嵌入式LDAP服务器，以测试在应用程序中的使用情况：

```
<security:ldap-server />
```

这比连接等效的 Apache 目录服务器bean要简单得多。最常见的替代配置需求由`ldap-server`元素上的属性支持，并且用户不必担心需要创建哪些bean以及 Bean 属性名称是什么。<sup class="footnote">[<a id="_footnoteref_1" class="footnote" href="#_footnotedef_1" title="View footnote.">1</a>]</sup>。]在编辑应用程序上下文文件时使用一个好的XML编辑器，应该提供关于可用的属性和元素的信息。我们建议你尝试[Eclipse IDE with Spring Tools](https://spring.io/tools)，因为它具有用于处理标准 Spring 名称空间的特殊功能。

要在应用程序上下文中开始使用安全名称空间，你需要在 Classpath 上使用`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
		https://www.springframework.org/schema/beans/spring-beans-3.0.xsd
		http://www.springframework.org/schema/security
		https://www.springframework.org/schema/security/spring-security.xsd">
	...
</beans>
```

在你将看到的许多示例中（以及在示例应用程序中），我们通常使用“security”作为默认名称空间，而不是“bean”，这意味着我们可以省略所有安全名称空间元素上的前缀，从而使内容更易于阅读。如果你将你的应用程序上下文划分为不同的文件，并且将大多数安全配置都放在其中一个文件中，那么你可能也想这样做。然后，你的安全应用程序上下文文件将像这样启动

```
<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
		https://www.springframework.org/schema/beans/spring-beans-3.0.xsd
		http://www.springframework.org/schema/security
		https://www.springframework.org/schema/security/spring-security.xsd">
	...
</beans:beans>
```

在本章中，我们将假设从现在开始使用这种语法。

### 命名空间的设计

名称空间旨在捕捉框架的最常见用途，并提供简化和简明的语法，以便在应用程序中启用它们。该设计基于框架内的大规模依赖关系，可以分为以下几个方面：

* *Web/HTTP安全性*-最复杂的部分。设置用于应用框架身份验证机制的过滤器和相关服务bean，以保护URL，呈现登录和错误页面等等。

* *业务对象（方法）安全性*-用于保护服务层的选项。

* *身份验证管理器*-处理来自框架其他部分的身份验证请求。

* *AccessDecisionManager*-提供Web和方法安全性的访问决策。将注册一个默认的，但你也可以选择使用一个自定义的，使用正常的 Spring  Bean 语法声明。

* *身份验证提供者*s-身份验证管理器对用户进行身份验证的机制。名称空间提供了对几个标准选项的支持，也是添加使用传统语法声明的自定义bean的一种方式。

* *UserDetailsService*-与身份验证提供程序密切相关，但通常也需要其他bean。

我们将在下面的小节中看到如何配置这些功能。

## 安全名称空间配置入门

在本节中，我们将研究如何构建名称空间配置，以使用框架的一些主要特性。让我们假设你最初希望尽快启动和运行，并通过一些测试登录向现有的Web应用程序添加身份验证支持和访问控制。然后，我们将研究如何转换为针对数据库或其他安全存储库进行身份验证。在后面的部分中，我们将介绍更高级的名称空间配置选项。

### web.xml配置

你需要做的第一件事是将以下过滤器声明添加到你的`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 安全Web基础设施的钩子。`DelegatingFilterProxy`是一个 Spring 框架类，它委托给一个过滤器实现，该过滤器实现在你的应用程序上下文中被定义为 Spring  Bean。在这种情况下， Bean 被命名为“SpringSecurityFilterChain”，这是由名称空间创建的内部基础设施 Bean，用于处理Web安全。请注意，你自己不应该使用这个名称。一旦你把这个添加到你的`web.xml`中，你就可以开始编辑你的应用程序上下文文件了。Web安全服务使用`<http>`元素进行配置。

### 最小\<http\>配置

要启用Web安全性，你所需要做的就是从一开始就启用它。

```
<http>
<intercept-url pattern="/**" access="hasRole('USER')" />
<form-login />
<logout />
</http>
```

这表示我们希望我们的应用程序中的所有URL都是安全的，需要角色`ROLE_USER`来访问它们，我们希望使用带有用户名和密码的表单登录到应用程序，并且我们希望注册一个注销URL，这将允许我们从应用程序中注销。`<http>`元素是所有与Web相关的命名空间功能的父元素。`<intercept-url>`元素定义了一个`pattern`，它使用 Ant 路径样式语法<sup class="footnote">[<a id="_footnoteref_2" class="footnote" href="#_footnotedef_2" title="View footnote.">2</a>]</sup>，用于匹配实际执行方式的更多详细信息。]你还可以使用正则表达式匹配作为一种选择（有关更多详细信息，请参见名称空间附录）。`access`属性定义了匹配给定模式的请求的访问要求。对于默认配置，这通常是一个逗号分隔的角色列表，其中一个必须允许用户提出请求。前缀“role\_”是一个标记，表示应该与用户的权限进行简单的比较。换句话说，应该使用正常的基于角色的检查。 Spring 安全中的访问控制并不限于使用简单的角色（因此使用前缀来区分不同类型的安全属性）。稍后我们将看到解释如何改变<sup class="footnote">[<a id="_footnoteref_3" class="footnote" href="#_footnotedef_3" title="View footnote.">3</a>]</sup>。在 Spring Security3.0中，还可以使用[EL表达式](../authorization/expression-based.html#el-access)填充属性。

|   |你可以使用多个`<intercept-url>`元素来为不同的URL集定义不同的访问要求，但它们将按照列出的顺序进行计算，并使用第一个匹配项。，因此，你必须将最具体的匹配项放在顶部，<br/>还可以添加一个`method`属性，以将匹配限制为特定的HTTP方法（`GET`，`POST`，`PUT`等）。|
|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

要添加一些用户，你可以直接在名称空间中定义一组测试数据：

```
<authentication-manager>
<authentication-provider>
	<user-service>
	<!-- Password is prefixed with {noop} to indicate to DelegatingPasswordEncoder that
	NoOpPasswordEncoder should be used. This is not safe for production, but makes reading
	in samples easier. Normally passwords should be hashed using BCrypt -->
	<user name="jimi" password="{noop}jimispassword" authorities="ROLE_USER, ROLE_ADMIN" />
	<user name="bob" password="{noop}bobspassword" authorities="ROLE_USER" />
	</user-service>
</authentication-provider>
</authentication-manager>
```

这是存储相同密码的安全方法的一个示例。密码的前缀是`{bcrypt}`，指示`DelegatingPasswordEncoder`，该命令支持任何配置的`PasswordEncoder`进行匹配，并使用bcrypt对密码进行哈希：

```
<authentication-manager>
<authentication-provider>
	<user-service>
	<user name="jimi" password="{bcrypt}$2a$10$ddEWZUl8aU0GdZPPpy7wbu82dvEw/pBpbRvDQRqA41y6mK1CoH00m"
			authorities="ROLE_USER, ROLE_ADMIN" />
	<user name="bob" password="{bcrypt}$2a$10$/elFpMBnAYYig6KRR5bvOOYeZr1ie1hSogJryg9qDlhza4oCw1Qka"
			authorities="ROLE_USER" />
	<user name="jimi" password="{noop}jimispassword" authorities="ROLE_USER, ROLE_ADMIN" />
	<user name="bob" password="{noop}bobspassword" authorities="ROLE_USER" />
	</user-service>
</authentication-provider>
</authentication-manager>
```

如果你熟悉该框架的预命名空间版本，你可能已经可以大致猜到这里发生了什么。`<http>`元素负责创建`FilterChainProxy`和它所使用的过滤器bean。常见的问题，如不正确的过滤器排序不再是一个问题，因为过滤器的位置是预先定义的。

`<authentication-provider>`元素创建一个`DaoAuthenticationProvider` Bean，而`<user-service>`元素创建一个`InMemoryDaoImpl`。所有`authentication-provider`元素必须是`<authentication-manager>`元素的子元素，该元素创建一个`ProviderManager`并向其注册身份验证提供者。你可以在[名称空间附录](../appendix/namespace/index.html#appendix-namespace)中找到有关创建的bean的更详细信息。如果你想开始了解框架中的重要类是什么以及它们是如何使用的，特别是如果你想在以后定制这些类，那么值得对此进行交叉检查。

上面的配置定义了两个用户、他们的密码和他们在应用程序中的角色（将用于访问控制）。还可以使用`user-service`上的`properties`属性从标准属性文件加载用户信息。有关文件格式的更多详细信息，请参见[内存中身份验证](../authentication/passwords/in-memory.html#servlet-authentication-inmemory)一节。使用`<authentication-provider>`元素意味着用户信息将被身份验证管理器用于处理身份验证请求。你可以使用多个`<authentication-provider>`元素来定义不同的认证源，并依次对每个元素进行查询。

此时，你应该能够启动你的应用程序，并且需要登录才能继续。尝试一下，或者尝试一下项目附带的“教程”示例应用程序。

#### 设置默认的登录后目标

如果试图访问受保护的资源时没有提示表单登录，那么`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`的实例。

## 高级Web功能

### 添加自己的过滤器

如果你以前使用过 Spring 安全性，那么你就会知道，该框架维护了一系列过滤器，以便应用其服务。你可能希望在特定位置将自己的筛选器添加到堆栈中，或者使用 Spring 安全筛选器，该筛选器目前没有名称空间配置选项（例如，CAS）。或者，你可能希望使用标准名称空间过滤器的定制版本，例如`UsernamePasswordAuthenticationFilter`，它是由`<form-login>`元素创建的，利用了一些额外的配置选项，这些额外的配置选项可以通过显式地使用 Bean 来获得。既然过滤器链不是直接公开的，那么如何使用名称空间配置来实现这一点呢？

在使用名称空间时，总是严格执行过滤器的顺序。在创建应用程序上下文时，过滤器bean按名称空间处理代码进行排序，标准 Spring 安全过滤器在名称空间和众所周知的位置中都有一个别名。

|   |在以前的版本中，排序是在应用程序上下文的后处理过程中，在过滤器实例被创建之后进行的，<br/>在版本3.0中，排序现在是在 Bean 元数据级别完成的，在类被实例化之前。<br/>这对于如何将自己的筛选器添加到堆栈有影响，因为在解析`<http>`元素期间，必须知道整个筛选器列表，所以语法在3.0中略有变化。|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

创建过滤器的过滤器、别名和名称空间元素/属性在[标准过滤器别名和排序](#filter-stack)中显示。过滤器是按照它们在过滤器链中出现的顺序列出的。

|            Alias             |                    Filter Class                     |名称空间元素或属性|
|------------------------------|-----------------------------------------------------|-------------------------------------------------------|
|       CHANNEL\_FILTER        |              `ChannelProcessingFilter`              |`http/[[email protected]](/cdn-cgi/l/email-protection)`|
|  SECURITY\_CONTEXT\_FILTER   |         `SecurityContextPersistenceFilter`          |`http`|
| CONCURRENT\_SESSION\_FILTER  |              `ConcurrentSessionFilter`              |`session-management/concurrency-control`|
|       HEADERS\_FILTER        |                `HeaderWriterFilter`                 |`http/headers`|
|         CSRF\_FILTER         |                    `CsrfFilter`                     |`http/csrf`|
|        LOGOUT\_FILTER        |                   `LogoutFilter`                    |`http/logout`|
|         X509\_FILTER         |             `X509AuthenticationFilter`              |`http/x509`|
|      PRE\_AUTH\_FILTER       |`AbstractPreAuthenticatedProcessingFilter` Subclasses|不适用|
|         CAS\_FILTER          |              `CasAuthenticationFilter`              |不适用|
|     FORM\_LOGIN\_FILTER      |       `UsernamePasswordAuthenticationFilter`        |`http/form-login`|
|     BASIC\_AUTH\_FILTER      |             `BasicAuthenticationFilter`             |`http/http-basic`|
|SERVLET\_API\_SUPPORT\_FILTER |      `SecurityContextHolderAwareRequestFilter`      |`http/@servlet-api-provision`|
|  JAAS\_API\_SUPPORT\_FILTER  |             `JaasApiIntegrationFilter`              |`http/@jaas-api-provision`|
|     REMEMBER\_ME\_FILTER     |          `RememberMeAuthenticationFilter`           |`http/remember-me`|
|      ANONYMOUS\_FILTER       |           `AnonymousAuthenticationFilter`           |`http/anonymous`|
| SESSION\_MANAGEMENT\_FILTER  |              `SessionManagementFilter`              |`session-management`|
|EXCEPTION\_TRANSLATION\_FILTER|            `ExceptionTranslationFilter`             |`http`|
|FILTER\_SECURITY\_INTERCEPTOR |             `FilterSecurityInterceptor`             |`http`|
|     SWITCH\_USER\_FILTER     |                 `SwitchUserFilter`                  |不适用|

你可以将自己的筛选器添加到堆栈中，使用`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`属性一起使用，以表明你希望你的筛选器分别出现在整个堆栈之前或之后。

|   |避免过滤器位置冲突<br/><br/>如果你要插入一个自定义过滤器，该过滤器的位置可能与名称空间创建的标准过滤器的位置相同，那么这一点很重要你没有错误地包含名称空间版本。，<br/>删除任何创建过滤器的元素，这些过滤器的功能是你想要替换的，<br/><br/>注意，你不能替换使用`<http>`元素本身创建的过滤器-`SecurityContextPersistenceFilter`，`ExceptionTranslationFilter`或`FilterSecurityInterceptor`。<br/>默认情况下添加了其他一些过滤器，但你可以禁用它们。<br/>默认情况下添加了一个`AnonymousAuthenticationFilter`，除非你禁用了[session-fixation protection](../authentication/session-management.html#ns-session-fixation)，否则还将向过滤器链中添加一个`SessionManagementFilter`。|
|---| |

如果要替换需要身份验证入口点的名称空间过滤器（即未经身份验证的用户试图访问安全资源而触发身份验证过程），还需要添加自定义入口点 Bean。

## 方法安全性

Spring 从版本2.0开始，安全性在很大程度上改进了对向服务层方法添加安全性的支持。它提供了对JSR-250注释安全性的支持，以及框架的原始`@Secured`注释。从3.0开始，你还可以使用New[基于表达式的注释](../authorization/expression-based.html#el-access)。你可以对单个 Bean 应用安全性，使用`intercept-methods`元素来装饰 Bean 声明，或者可以使用AspectJ样式的切入点在整个服务层中保护多个bean。

## 默认的accessesdecisionManager

本节假定你对 Spring Security中的访问控制的底层体系结构有一定的了解。如果你不这样做，你可以跳过它，稍后再来讨论它，因为这一部分只与那些需要进行一些定制以使用比简单的基于角色的安全性更多功能的人相关。

当你使用名称空间配置时，将自动为你注册一个`AccessDecisionManager`的默认实例，该实例将用于为方法调用和Web URL访问做出访问决策，基于你在`intercept-url`和`protect-pointcut`声明中指定的访问属性（如果你使用的是注释安全方法，则在注释中指定）。

默认的策略是使用`AffirmativeBased``AccessDecisionManager`和`RoleVoter`。你可以在[授权](../authorization/architecture.html#authz-arch)一章中找到更多有关这些的信息。

### 自定义accessDecisionManager

如果需要使用更复杂的访问控制策略，那么很容易为方法和Web安全性设置替代方案。

对于方法安全性，你可以通过将`global-method-security`上的`access-decision-manager-ref`属性设置为应用程序上下文中适当的`id` Bean 中的`id`来实现此目的：

```
<global-method-security access-decision-manager-ref="myAccessDecisionManagerBean">
...
</global-method-security>
```

Web安全性的语法是相同的，但是在`http`元素上：

```
<http access-decision-manager-ref="myAccessDecisionManagerBean">
...
</http>
```

---

[1](#_footnoteref_1)。你可以在关于Xref的章节中找到有关`ldap-server`元素使用情况的更多信息： Servlet/authentication/unpwd/ldap.ADOC# Servlet-authentication-ldap[ldap身份验证]

[2](#_footnoteref_2)。参见Xref部分： Servlet/exploits/firewall.ADOC# Servlet-HttpFirewall[`HttpFirewall`

[3](#_footnoteref_3)。对`access`属性中逗号分隔的值的解释取决于所使用的[AccessDecisionManager](#ns-access-manager)的实现。

[Kotlin Configuration](kotlin.html)[Testing](../test/index.html)

