Basic and Digest Authentication for a REST Service with Spring Security – 用Spring Security对REST服务进行基本和摘要验证

最后修改: 2011年 11月 20日

中文/混合/英文(键盘快捷键:t)

Table of Contents

目录

1. Overview

1.概述

This article discusses how to set up both Basic and Digest Authentication on the same URI structure of a REST API. In a previous article, we discussed another method of securing the REST Service – form-based authentication, so Basic and Digest authentication is the natural alternative, as well as the more RESTful one.

本文讨论了如何在REST API的同一URI结构上设置基本认证和摘要认证。在之前的文章中,我们讨论了另一种保护REST服务的方法–基于表单的认证,因此基本认证和摘要认证是自然的选择,也是更为REST的选择。

2. Configuration of Basic Authentication

2.基本认证的配置

The main reason that form-based authentication is not ideal for a RESTful Service is that Spring Security will make use of Sessions – this is of course state on the server, so the statelessness constraints in REST is practically ignored.

基于表单的认证对于RESTful服务来说并不理想,主要原因是Spring Security将使用会话–这当然是服务器上的状态,所以REST中的无状态约束实际上被忽略了。

We’ll start by setting up Basic Authentication – first we remove the old custom entry point and filter from the main <http> security element:

我们将从设置基本认证开始–首先我们从主<http>安全元素中删除旧的自定义入口点和过滤器。

<http create-session="stateless">
   <intercept-url pattern="/api/admin/**" access="ROLE_ADMIN" />

   <http-basic />
</http>

Note how support for basic authentication has been added with a single configuration line – <http-basic /> – which handles the creation and wiring of both the BasicAuthenticationFilter and the BasicAuthenticationEntryPoint.

请注意,对基本认证的支持是如何通过一个配置行添加的–<http-basic />–它处理BasicAuthenticationFilterBasicAuthenticationEntryPoint的创建和布线。

2.1. Satisfying the Stateless Constraint – Getting Rid of Sessions

2.1.满足无状态约束 – 摆脱会话

One of the main constraints of the RESTful architectural style is that the client-server communication is fully stateless, as the original dissertation reads:

RESTful架构风格的主要约束之一是客户机-服务器通信是完全无状态的,正如原始论文所言。

5.1.3 Stateless

5.1.3 无状态

We next add a constraint to the client-server interaction: communication must be stateless in nature, as in the client-stateless-server (CSS) style of Section 3.4.3 (Figure 5-3), such that each request from client to server must contain all of the information necessary to understand the request, and cannot take advantage of any stored context on the server. Session state is therefore kept entirely on the client.

我们接下来给客户机-服务器的交互添加一个约束:通信必须是无状态的,就像第3.4.3节的客户机-无状态-服务器(CSS)风格一样(图5-3),这样,从客户机到服务器的每个请求必须包含理解请求的所有必要信息,并且不能利用服务器上的任何存储上下文。因此,会话状态完全保存在客户端

The concept of Session on the server is one with a long history in Spring Security, and removing it entirely has been difficult until now, especially when the configuration was done by using the namespace.

服务器上的Session概念在Spring Security中由来已久,直到现在还很难完全删除它,特别是在使用命名空间进行配置时。

However, Spring Security augments the namespace configuration with a new stateless option for session creation, which effectively guarantees that no session will be created or used by Spring. What this new option does is complete removes all session related filters from the security filter chain, ensuring that authentication is performed for each request.

然而,Spring Security 增强命名空间配置,为会话创建提供新的无状态选项,这有效地保证Spring将不会创建或使用会话。这个新选项的作用是将所有与会话相关的过滤器从安全过滤器链中完全移除,确保对每个请求都进行验证。

3. Configuration of Digest Authentication

3.配置Digest认证

Starting with the previous configuration, the filter and entry point necessary to set up digest authentication will be defined as beans. Then, the digest entry point will override the one created by <http-basic> behind the scenes. Finally, the custom digest filter will be introduced in the security filter chain using the after semantics of the security namespace to position it directly after the basic authentication filter.

从以前的配置开始,设置摘要认证所需的过滤器和入口点将被定义为Bean。然后,digest入口点将覆盖<http-basic>在幕后创建的入口点。最后,自定义的digest过滤器将被引入安全过滤器链中,使用安全命名空间的after语义,将其直接置于基本认证过滤器之后。

<http create-session="stateless" entry-point-ref="digestEntryPoint">
   <intercept-url pattern="/api/admin/**" access="ROLE_ADMIN" />

   <http-basic />
   <custom-filter ref="digestFilter" after="BASIC_AUTH_FILTER" />
</http>

<beans:bean id="digestFilter" class=
 "org.springframework.security.web.authentication.www.DigestAuthenticationFilter">
   <beans:property name="userDetailsService" ref="userService" />
   <beans:property name="authenticationEntryPoint" ref="digestEntryPoint" />
</beans:bean>

<beans:bean id="digestEntryPoint" class=
 "org.springframework.security.web.authentication.www.DigestAuthenticationEntryPoint">
   <beans:property name="realmName" value="Contacts Realm via Digest Authentication"/>
   <beans:property name="key" value="acegi" />
</beans:bean>

<authentication-manager>
   <authentication-provider>
      <user-service id="userService">
         <user name="eparaschiv" password="eparaschiv" authorities="ROLE_ADMIN" />
         <user name="user" password="user" authorities="ROLE_USER" />
      </user-service>
   </authentication-provider>
</authentication-manager>

Unfortunately, there is no support in the security namespace to automatically configure the digest authentication the way basic authentication can be configured with <http-basic>. Because of that, the necessary beans had to be defined and wired manually into the security configuration.

不幸的是,安全命名空间中没有支持来自动配置摘要认证,而基本认证可以用<http-basic>配置。正因为如此,必须对必要的Bean进行定义,并将其手动连接到安全配置中。

4. Supporting Both Authentication Protocols in the Same Restful Service

4.在同一个Restful服务中支持两种认证协议

Basic or Digest authentication alone can be easily implemented in Spring Security; it is supporting both of them for the same RESTful web service, on the same URI mappings that introduces a new level of complexity into the configuration and testing of the service.

在Spring Security中可以很容易地实现基本认证或摘要认证;在同一个URI映射上为同一个RESTful Web服务支持这两种认证,会给服务的配置和测试带来新的复杂性。

4.1. Anonymous Request

4.1.匿名请求

With both basic and digest filters in the security chain, the way an anonymous request – a request containing no authentication credentials (Authorization HTTP header) – is processed by Spring Security is – the two authentication filters will find no credentials and will continue execution of the filter chain. Then, seeing how the request wasn’t authenticated, an AccessDeniedException is thrown and caught in the ExceptionTranslationFilter, which commences the digest entry point, prompting the client for credentials.

有了安全链中的基本和摘要过滤器,Spring Security处理匿名请求–不包含认证凭证(Authorization HTTP头)的方式是–两个认证过滤器会发现没有凭证,并继续执行过滤器链。然后,看到请求没有被认证,就会抛出一个AccessDeniedException,并在ExceptionTranslationFilter中捕获,这就开始了摘要入口,提示客户端提供凭证。

The responsibilities of both the basic and digest filters are very narrow – they will continue to execute the security filter chain if they are unable to identify the type of authentication credentials in the request. It is because of this that Spring Security can have the flexibility to be configured with support for multiple authentication protocols on the same URI.

基本过滤器和摘要过滤器的职责都非常狭窄–如果它们无法识别请求中的认证凭证类型,它们将继续执行安全过滤器链。正因为如此,Spring Security可以灵活配置,在同一个URI上支持多种认证协议。

When a request is made containing the correct authentication credentials – either basic or digest – that protocol will be rightly used. However, for an anonymous request, the client will get prompted only for digest authentication credentials. This is because the digest entry point is configured as the main and single entry point of the Spring Security chain; as such digest authentication can be considered the default.

当一个请求包含正确的认证凭证–无论是基本认证还是摘要认证–该协议将被正确地使用。然而,对于一个匿名请求,客户端将只被提示提供摘要认证凭证。这是因为摘要入口点被配置为Spring安全链的主要和单一入口点;因此摘要认证可被视为默认的

4.2. Request With Authentication Credentials

4.2.带有认证凭证的请求

A request with credentials for Basic authentication will be identified by the Authorization header starting with the prefix “Basic”. When processing such a request, the credentials will be decoded in the basic authentication filter and the request will be authorized. Similarly, a request with credentials for Digest authentication will use the prefix “Digest” for it’s Authorization header.

带有基本认证证书的请求将由Authorization头来识别,其前缀为“Basic”。当处理这样的请求时,凭证将在基本认证过滤器中被解码,请求将被授权。同样地,一个带有Digest认证证书的请求将在它的“Digest”头中使用前缀Authorization

5. Testing Both Scenarios

5.测试两种情况

The tests will consume the REST service by creating a new resource after authenticating with either basic or digest:

测试将通过在用基本或摘要认证后创建一个新资源来消费REST服务。

@Test
public void givenAuthenticatedByBasicAuth_whenAResourceIsCreated_then201IsReceived(){
   // Given
   // When
   Response response = given()
    .auth().preemptive().basic( ADMIN_USERNAME, ADMIN_PASSWORD )
    .contentType( HttpConstants.MIME_JSON ).body( new Foo( randomAlphabetic( 6 ) ) )
    .post( paths.getFooURL() );

   // Then
   assertThat( response.getStatusCode(), is( 201 ) );
}
@Test
public void givenAuthenticatedByDigestAuth_whenAResourceIsCreated_then201IsReceived(){
   // Given
   // When
   Response response = given()
    .auth().digest( ADMIN_USERNAME, ADMIN_PASSWORD )
    .contentType( HttpConstants.MIME_JSON ).body( new Foo( randomAlphabetic( 6 ) ) )
    .post( paths.getFooURL() );

   // Then
   assertThat( response.getStatusCode(), is( 201 ) );
}

Note that the test using basic authentication adds credentials to the request preemptively, regardless if the server has challenged for authentication or not. This is to ensure that the server doesn’t need to challenge the client for credentials, because if it did, the challenge would be for Digest credentials, since that is the default.

请注意,使用基本认证的测试会在请求中先发制人地添加凭证,而不管服务器是否已经挑战了认证。这是为了确保服务器不需要向客户端挑战凭证,因为如果它这样做了,挑战就会是Digest凭证,因为那是默认的。

6. Conclusion

6.结论

This article covered the configuration and implementation of both Basic and Digest authentication for a RESTful service, using mostly Spring Security namespace support as well as some new features in the framework.

本文介绍了RESTful服务的Basic和Digest认证的配置和实现,主要使用Spring Security命名空间支持以及框架中的一些新特性。