案例概述

本文讨论怎么在REST API的相同URI结构上设置根本身份验证和Digest身份验证。在前一篇文章中,咱们讨论了另一种维护REST服务的办法 – 根据表单的身份验证,因而Basic和Digest身份验证是天然的替代方案,也是更RESTful的身份验证办法。

根本的认证装备

根据表单的身份验证不适合RESTful服务的首要原因是Spring Security将运用Sessions – 这当然是服务器上的状况,因而REST中的无状况约束简直被疏忽。

咱们首先设置根本身份验证 – 首先咱们从主安全元素中删去旧的自定义进口点和过滤器:

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

请注意,怎么运用单个装备行添加对根本身份验证的支撑 – – 它处理BasicAuthenticationFilter和BasicAuthenticationEntryPoint的创立和连接。

满意无状况约束—摆脱Session

RESTful架构风格的首要约束之一是客户端 – 服务器通讯是完全无状况的,正如原始论文所示:

5.1.3 无状况:
接下来咱们为客户端 – 服务器交互添加一个约束:通讯本质上有必要是无状况的,如3.4.3节(图5-3)的客户端无状况服务器(CSS)样式,这样每个恳求从客户端到服务器有必要包括理解恳求所需的一切信息,而且不能运用服务器上任何存储的上下文。因而,会话状况完全保留在客户端上。

服务器上的Session概念在Spring Security中具有悠长的历史,而且直到现在才完全删去它,尤其是在运用命名空间完结装备时。

但是,Spring Security 运用新的无状况选项增强了命名空间装备,从而有效地保证Spring不会创立或运用任何会话。此新选项的作用是从安全挑选器链中删去一切与会话相关的挑选器,保证对每个恳求履行身份验证。

Digest式身份验证的装备

从从前的装备开始,设置Digest式身份验证所需的过滤器和进口点将被定义为bean。然后,Digest进口点将掩盖由暗地的创立的进口点。最后,将运用安全命名空间的语义在安全过滤器链中引入自定义Digest过滤器,以在根本身份验证过滤器之后直接定位它。

<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>

遗憾的是,安全命名空间中不支撑主动装备Digest式身份验证,这与运用装备根本身份验证的方法相同。因而,有必要定义必要的bean并手动连接到安全装备中。

在同一RESTful服务中支撑两种身份验证协议

仅在Spring Security中能够轻松完成根本或Digest式身份验证; 它支撑同一个RESTful Web服务,在相同的URI映射上为服务的装备和测验引入了新的复杂性。

匿名恳求

经过安全链中的根本和Digest过滤器,Spring Security处理匿名恳求(不包括身份验证凭据的恳求(授权HTTP标头))的方法是 – 两个身份验证过滤器将找不到凭据并将持续履行过滤链。然后,查看恳求未经过身份验证的方法,抛出AccessDeniedException并将其捕获到ExceptionTranslationFilter中,该ExceptionTranslationFilter将启动Digest进口点,提示客户端提供凭据。

根本和Digest过滤器的责任非常狭窄 – 如果他们无法识别恳求中的身份验证凭据类型,他们将持续履行安全过滤器链。正由于如此,Spring Security能够灵敏地装备在同一URI上支撑多个身份验证协议。

当恳求包括正确的身份验证凭据(根本或Digest)时,将正确运用该协议。但是,对于匿名恳求,将仅提示客户端提取Digest身份验证凭据。这是由于Digest进口点被装备为Spring Security链的首要和单个进口点; 由于这样的Digest认证能够被认为是默许的

恳求身份验证凭据

具有根本身份验证凭据的恳求将由以“Basic”前缀开头的Authorization标头标识。在处理这样的恳求时,凭据将在根本认证过滤器中被解码,而且该恳求将被授权。同样,具有Digest式身份验证凭据的恳求将运用前缀“Digest”作为其授权标头。

测验两种状况

在运用根本或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 ) );
}

请注意,运用根本身份验证的测验无论服务器是否已经过验证都会抢先向恳求添加凭据。这是为了保证服务器不需要向客户端恳求凭据,由于这样,才会运用摘要凭据,这是默许值。

案例结论

本文介绍了RESTful服务的Basic和Digest身份验证的装备和完成,首要运用Spring Security命名空间支撑以及结构中的一些新功能。