1. 简介
这篇文章讨论的是如何在一个相同URI结构的REST API上使用基础和摘要式的认证。在一篇从前的文章里,我们讨论了另一种保护REST服务的方法----表单为基础的认证,但是基础的和摘要式的认证是另一个更自然的方式,也是更RESTful的一种。
2. 基础认证的配置
表单为基础的认证不是RESTful服务的一个合适认证方式的主要原因是Spring Security将会使用Sessions -- 这当然是一个服务器的状态,所以REST服务是一种无状态的服务这一原则实际上是被违背了。
我们将开始设置基础认证 -- 首先我们从主要的<http> security 元素中移除老的自定义入口和过滤器,
1
2
3
4
5
|
<
http
create-session
=
"stateless"
>
<
intercept-url
pattern
=
"/api/admin/**"
access
=
"ROLE_ADMIN"
/>
<
http-basic
/>
</
http
>
|
注意如何增加简单的一行配置<http-basic />来开启基础认证 -- 它是处理创建和装配BasicAuthenticationFilter和BasicAuthenticationEntryPoint。
2.1. 满足无状态的约束 - 摆脱sessions
RESTful风格一个主要的约束就是客户与服务器的通讯是完全无状态的(stateless),就像论文中写的:
5.1.3 无状态
我们接下来增加一条客户端-服务器交互的约束: 通讯必须是无状态的,在章节3.4.3中的无状态的 客户端-服务器风格里,每一次从客户端到服务器的请求必须包含所有必须的信息来理解此请求,并且不能使用任何存储在服务器中的内容。Session状态因此必须完整的保持在客户端上。
服务器中的Session在Spring Security中是一个历史悠久的概念,想要完全的移除它现在看来是很困难的,特别是当配置命名空间时。然而,Spring Security 3.1为session的创建扩展了一个新的无状态的命名空间(namespace)选项,这可以有效地保证Spring的无session化。这个新选项的作用是在security filter chain相关的filters中完全移除所有的session,保证每一个请求都会进行身份认证。
3. 配置摘要式认证
接着前边的配置继续,filter 和entry point 需要设置摘要式认证的定义beans。然后,digest entry point将会在后台覆盖<http-basic>创建的entry point。最后,自定义的digest filter将会织入到security filter chain,并直接放置在基础认证过滤的后边。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
<
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
>
|
4. 在一个RESTful服务中同时支持两种认证协议
单独的基础认证和摘要认证可以很容易地被 Spring Security 3.x实现;它也支持在同一个RESTful服务中同时使用,在同一个URI中进行映射提升了配置和测试这个服务的复杂性。
4.1. 匿名请求
在 security chain中同时使用基础和摘要认证的情况下,匿名请求的方式 --- 一种不包含任何认证凭据的请求(Authorization HTTP header) --- 被Spring Security处理 --- 两种认证过滤会找不到任何凭据并继续执行过滤链。然后,我们会看到一个请求如果没有认证通过,一个AccessDeniedException 会被抛出并获取在ExceptionTranslationFilter中,跳转到摘要入口,提示客户端输入凭据。
基础认证和摘要认证的功能是非常有限的,如果不能定义请求中认证凭据的类型他们会继续执行安全过滤链。所以Spring Security可以非常灵活的在同一URI上配置多重认证协议配置。
当一个请求包含正确的认证凭据时 - 基础的或者摘要的 - 协议都可以被正确的使用。然而,任何匿名的请求,客户端将会被提示摘要认证凭据。这事因为摘要入口被配置为主要的、单一的 Spring Security链入口;所以,摘要认证可以被考虑为默认的认证入口。
4.2. 带认证凭据的请求
基础认证定义一个带凭据的请求是通过Authorization中带“Basic”前缀的请求头。当处理这样一个请求时,凭据将会被解码到基础认证过滤器中并且请求将会被认证。类似的,一个带凭据的摘要请求使用的是带“Digest”前缀的Authorization 请求头。
5. 测试上面的场景
测试将会消费REST服务,通过在基础/摘要认证后创建的一个新资源:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
@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
) );
}
|
6. 结论
这篇文章覆盖了为RESTful服务配置和实现基础认证和摘要认证,使用了大多数的Spring Security 3.0命名空间支持和一些 by Spring Security 3.1新增的新特性。
查看完整的实现,请 check out the github project.
转载自 http://my.oschina.net/buwei/blog/193576