5

写在开篇:

在一边翻译,一边学习的过程中,感觉还是将原文改造为更为精小的课程会更为合适,原因在于:

  1. 这本书本来就是实践课程,本书的目的也是希望大家通过应用,来加深对Spring Security的理解。
  2. 原文讲述的比较细致,甚至过于细致,例如maven的按照过程,这样的,去除这部分,能更加突出重点。

在改造课程的过程中,我会做到:

  1. 翻译尊重原文,适当意译。
  2. 原文提到的知识点我会全部保留
  3. 所有用例我会做一遍,并对一些部分进行适当补充,或改动。

再次声明,本改编课程源于《OAuth 2.0 Cookbook_Protect Your Web Applications using Spring Security-Packt Publishing(2017)》

课程从第二章开始,在Chaptor2,我们将学习以下内容:

  1. 使用授权码模式(Authorization Code grant)保护资源

  2. 支持隐式授权模式(Implicit grant)
  3. 使用密码模式(Resource Owner Password Credentials grant type )
  4. 配置客户端证书授权模式(Client Credentials grant)
  5. 支持refresh tokens
  6. 使用一个关系数据库来保存tokens和客户信息
  7. 使用redis保存token
  8. 实现客户端注册过程
  9. 中途破坏Oauth 2.0 Provider
  10. 使用Gatling,通过共享数据库测试token的验证过程

介绍

我们有一些场景,要求应用在和大量服务进行交互的同时,还要作为分布式系统在网络中提供API。在这样的场景下,用户如果要使用我们的应用程序授权给第三方应用程序,OAuth2.0是一个好的选择。

在这一章,你将学习如何使用OAuth2.0规范所描述的所有授予类型,来创建、配置和分发OAuth2.0 Provider,以覆盖不同场景,同时还将学到如何通过关系数据库和Redis来使用不同的访问令牌管理策略。本章中使用的所有用例都将通过Spring Ontecurity OAuth2.0来实现,在编写这本书的时候,它的版本是2.2.0.RELEASE(在http://projects.spring.io/spr... 中可以查看Spring Ontecurity OAuth2的官方文档)。学习如何配置自己的OAuth2.0 Provider 很重要,因为在当今的应用程序中集成了大量的应用程序。此外,通过阅读本章,您将能够通过使用Spring Security OAuth2.0,实践所有OAuth2细节。

记住,请在在生产中使用TLS/SSL来始终保护客户端和OAuth2.0 Provider之间的所有传输数据,并且贯穿本书的整个实践过程。

Getting ready(配置环境)

安装 java8,maven ,postman/curl

本例使用Spring Security OAuth2 Framework 来实现,此例将不使用任何数据库,我们将用户信息,授权口令都保存在内存中。本例源码可以从https://github.com/PacktPubli... 下载

How to do it

以下步骤将引导你使用Spring Security OAuth2.0 ,来成功配置一个验证服务器和一个资源服务器。

1.使用Spring Initializr (http://start.spring.io)新建一个项目auth-code-server ,增加web,security依赖

2.打开pom.xml,添加如下依赖:

<dependency>
  <groupId>org.springframework.security.oauth</groupId>
  <artifactId>spring-security-oauth2</artifactId>
  <version>2.2.0.RELEASE</version>
</dependency>

注:本例使用的springboot版本为1.5.4.RELEASE,请格外注意版本。
3.打开application.properties ,添加如下内容类来配置auth-code-server 的用户

security.user.name=adolfo
security.user.password=123

4.由于我们想保护用户的资源,所以,现在我们来创建一些待保护的资源,本例中是用户的name和email:

//UserProfile.java
public class UserProfile {
  private String name;
  private String email;
  //getters and setters hidden for brevity
}

5.新建文件UserController.java,提供endpoint获取用户信息,该endpoint被OAuth2.0保护(在第7步设置)。注意其中的User来自org.springframework.security.core.userdetails

//UserController.java
@Controller
public class UserController {
  @RequestMapping("/api/profile")
  public ResponseEntity<UserProfile> profile() {
    User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    String email = user.getUsername() + "@mailinator.com";
    UserProfile profile = new UserProfile();
    profile.setName(user.getUsername());
    profile.setEmail(email);
    return ResponseEntity.ok(profile);
  }
}

6.现在,已经存在需要被保护的endpoint,那么,我们需要创建OAuth2.0 授权服务,来对访问这个endpoint的请求进行授权

//OAuth2AuthorizationServer.java
@Configuration
@EnableAuthorizationServer
public class OAuth2AuthorizationServer extends AuthorizationServerConfigurerAdapter {
  @Override
  public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
    clients.inMemory()
           .withClient("clientapp")
           .secret("123456")
           .redirectUris("http://localhost:9000/callback")
           .authorizedGrantTypes("authorization_code")
           .scopes("read_profile", "read_contacts");
  }
}

7.做到第六步,这个应用已经可以通过分发access tokens,给用户授权许可。但是如果想获取用户资源(本例中是Resource Owner profile ),还需要创建资源服务器,用于设置需要被保护的endpoint,放过验证的endpoint等等。下面我们来新建OAuth2ResourceServer类.

//OAuth2ResourceServer.java
@Configuration
@EnableResourceServer
public class OAuth2ResourceServer extends ResourceServerConfigurerAdapter{
  @Override
  public void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests().anyRequest().authenticated().and()
                .requestMatchers().antMatchers("/api/**");
}
} 

至此,我们已经成功搭建了一个资源管理器和一个授权中心。并通过配置,保护匹配/api/**的请求。

How it works…

如果程序运行正确,我们能从http://localhost:8080/api/profile 拿到用户信息。正常情况下,我们拿到access token,往这个endpoint发请求,可以拿到这样的json字符串:

 {
   "name": "adolfo",
   "email": "adolfo@mailinator.com"
 } 

当然一开始,我们是没有access token的,所以,无论怎样访问,都会提示我们没有验证。那么,我们要如何获取access token呢。这个token是用户批准后获得,在本例中,我们在资源服务器加上 @EnableResourceServer的标签的同时,意味着,我们将OAuth2AuthenticationProcessingFilter配置进了Spring SecurityFilterChain(过滤链). 这个filter将会用来验证任何匹配/api/**`的请求中,是否携带access token,以及这个token是否是授权中心派发出去的,是否过期等等.

除了资源服务器的配置之外,我们也对授权服务器做了相关配置。可能你发现授权服务器中的代码量很少。使用Spring Security OAuth2.0 框架来配置授权服务器的确很简单,但是在简单的表相背后,隐藏了大量的配置。
通过添加@EnableAuthorizationServer这个注解,我们实际上导入了一些重要的配置文件(类):

  • AuthorizationServerEndpointsConfiguration
    AuthorizationServerSecurityConfiguration
    AuthorizationServerEndpointsConfiguration

AuthorizationServerEndpointsConfiguration类对OAuth2.0授权中心有很重要的作用,它定义了这些bean:

  • AuthorizationEndpoint
    TokenEndpoint
    CheckTokenEndpoint

这些bean声明了OAuth2.0的相关endpoint,这些endpoint用于引导验证流程,获取access token和刷新令牌请求。如果你想深入了解每个endpoint是如何工作的,我建议你读一下Spring Security OAuth2 的源码(github可以获取)。通过运行这个例子,我们能够与一些endpoint交互。现在,我们来运行这个例子:
1.启动项目,通过浏览器往服务器发送一下请求:

http://localhost:8080/oauth/authorize?client_id=clientapp&redirect_uri=http://localhost:9000/callback&response_type=code&scope=read_profile

因为我们使用的是授权码模式,我们需要将Resource owner重定向到验证页面,这一过程是在“/oauth/authorize”的endpoint声明的。注意到,我们发送了client_id, redirect_uriresponse_type参数。
如果你想更深入地了解每个参数的意思,可以查看https://tools.ietf.org/html/r...。注意,我们没有发送state参数,用以避免CSRF攻击(我们将会在第8章了解更多详情)。

2.输入我们在application.properties里配置的用户名和密码,
图片描述

点击login之后,你将被重定向到用户同意页,其中资源所有者可以选择授予第三方应用程序(客户端)的权限的范围。以下页面由Spring security OAuth2.0自动生成,当然也可以轻松被替换为自定义的页面。
图片描述

3.点击授权后,用户才能重定向回应用程序,这里的重定向地址由redirect_uri 参数决定,如果第一步发送的请求中带有redirect_uri,则会与我们在OAuth2AuthorizationServer类中定义的redirectUris进行匹配验证,如果没有,则直接使用我们在OAuth2AuthorizationServer中定义的redirectUris

授权之后,重定向请求将带有一个授权码,该授权代码描述用户对指定资源服务器上的指定客户端授予许可的权限。这个授权码看上去是这样的:

http://localhost:9000/callback?code=5sPk8A 

4.这个授权码是授权中心随机生成的,客户端用它向授权中心请求access token,可以使用curl工具:

   curl -X POST --user clientapp:123456 http://localhost:8080/oauth/token -H "content-type: application/x-www-form-urlencoded" -d "code=5sPk8A&grant_type=authorization_code&redirect_uri=http%3A%2F%2Flocalhost%3A9000%2Fcallback&scope=read_profile"

或者使用postman:
图片描述

图片描述

之后,我们能收到这样的回复:

{
  "access_token": "43c6f525-041f-43c8-b970-  82ba435d3c2c",
  "token_type": "bearer",
  "expires_in": 43199,
  "scope": "read_profile"
} 

这样,我们就拿到了access token.

5.我们现在再用携带access token的url去资源所有者请求资源,同样的,我们可以使用curl:

curl -X GET http://localhost:8080/api/profile -H "authorization: Bearer 43c6f525-041f-43c8-b970-82ba435d3c2c"

或者postman:
图片描述

这样,我们就拿到想要的资源了。

There's more...

这个例子尽可能编写的简单,但是当我们正式配置一个OAuth 2.0 Provider的时候,应该考虑使用数据库来替换内存来保存客户端的信息。
同样的,我们再次建议使用TLS/SSL来保护客户端与OAuth 2.0 Provider之间的交互。


Lavender
196 声望59 粉丝

喜欢开发,就酱紫~