为一个REST服务使用Spring Security的基本和摘要认证

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 />来开启基础认证 -- 它是处理创建和装配BasicAuthenticationFilterBasicAuthenticationEntryPoint。

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 >
    不幸的是,没有像配置基础认证使用 <http-basic>一样的命名空间方式来配置摘要式认证。正因如此,必要的beans不得不被手工定义和装配到 security 配置中。

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,这是一个关于使用Spring Security一个授权服务的问题。在这里,我会简单介绍一下Spring Security以及如何使用它来编写授权服务。 首先,Spring Security一个基于Spring框架的安全框架,它提供了一些常见的安全功能,例如身份验证、授权、攻击防御等。使用Spring Security,可以轻松地保护Web应用程序和REST API。 要编写一个授权服务,可以按照以下步骤进行: 1. 添加Spring Security依赖。可以在pom.xml中添加以下依赖: ``` <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> ``` 2. 创建一个Spring Security配置类,并添加@EnableWebSecurity注解。例如: ``` @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { //... } ``` 3. 在配置类中覆盖configure方法,配置身份验证和授权规则。例如: ``` @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/admin/**").hasRole("ADMIN") .antMatchers("/user/**").hasRole("USER") .and() .formLogin(); } ``` 上面的代码表示,访问/admin/**的URL需要ADMIN角色,访问/user/**的URL需要USER角色。并且使用表单登录进行身份验证。 4. 配置用户信息。可以使用内存身份验证、JDBC身份验证或LDAP身份验证等方式配置用户信息。例如,使用内存身份验证配置用户信息: ``` @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication() .withUser("admin").password("{noop}admin").roles("ADMIN") .and() .withUser("user").password("{noop}user").roles("USER"); } ``` 上面的代码表示,有两个用户admin和user,密码分别为admin和user,admin用户拥有ADMIN角色,user用户拥有USER角色。 5. 启动应用程序,并访问受保护的URL。当访问/admin/**的URL时,会弹出表单进行身份验证。输入admin用户的用户名和密码,就可以访问/admin/**的URL了。 通过以上步骤,我们可以使用Spring Security编写一个授权服务。当然,实际应用中可能会更加复杂,需要更多的配置和定制。希望对您有所帮助!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值