8.3 利用资源服务器保护 API

资源服务器实际上只是一个位于 API 前面的过滤器,确保需要授权的资源包括具有所需范围的有效访问令牌。Spring Security 提供了一个 OAuth2 资源服务器实现,您可以将其添加到现有的 API,将以下依赖项添加到 tacocloud-security 项目的构建中:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>

您还可以通过选择“OAuth2 Resource Server”来添加资源服务器依赖项,如果创建项目时使用的是 Initialzr。

有了依赖关系后,下一步是声明发送到 /ingredients 的 POST 请求需要“writeIngredients”作用域,并且对 /ingredients 的 DELETE 请求需要“deleteIngredients”作用域。下面摘录的是来自 tacocloud-security 项目的 SecurityConfig 类,显示了如何完成此配置:

@Override
protected void configure(HttpSecurity http) throws Exception {
  http
    .authorizeRequests()
  ...
    .antMatchers(HttpMethod.POST, "/api/ingredients")
      .hasAuthority("SCOPE_writeIngredients")
    .antMatchers(HttpMethod.DELETE, "/api//ingredients")
      .hasAuthority("SCOPE_deleteIngredients")
  ...
}

对于每个接口,都会调用 .hasAuthority() 方法来指定所需的作用域。请注意,作用域的前缀为“SCOPE_”,表示它们应该匹配针对请求这些资源时给出的访问令牌中的 OAuth 2 作用域。

在这个配置类中,我们还需要启用资源服务器:

@Override
protected void configure(HttpSecurity http) throws Exception {
  http
    ...
      .and()
        .oauth2ResourceServer(oauth2 -> oauth2.jwt())
  ...
}

这里为 oauth2ResourceServer() 方法提供了一个 lambda,用于配置资源服务器。在这里,它只是启用 JWT 令牌(与不透明令牌相反),以便资源服务器可以检查令牌的内容,以查找它包含哪些安全声明。具体来说,它将查看令牌是否包括“writeIngredients”和/或 “deleteIngredients”作用域,这是我们所保护的两个接口所适用的作用域。

不过,它不会按设置值直接信任令牌。要确保令牌是由受信的授权服务器生成的,它将使用与创建令牌签名私钥匹配的公钥来进行验证。我们需要配置资源服务器知道从何处获取公钥,请执行以下操作。以下属性将指定资源服务器访问的授权服务器上的 JWK 的 URL,并从那里获取公钥:

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          jwk-set-uri: http://localhost:9000/oauth2/jwks

现在我们的资源服务器已经准备好了!构建 Taco Cloud 应用程序并启动它。您可以像这样使用 curl 进行尝试:

$ curl localhost:8080/ingredients \
  -H"Content-type: application/json" \
  -d'{"id":"CRKT", "name":"Legless Crickets", "type":"PROTEIN"}'

请求应该会失败,并返回 HTTP 401 响应码。这是因为我们已经配置了接口需要“writeIngredients”作用域,但我们在请求上尚未提供有效作用域的访问令牌。

要想请求成功并添加新的配料,您需要使用我们在上一节中使用的流,获得相应的访问令牌,确保我们的请求在将浏览器转到授权服务器时具有“writeIngredients”和“deleteIngreients”作用域。然后,像这样使用 curl 在“Authorization”请求头中提供访问令牌 (用“$token”代替实际访问令牌):

$ curl localhost:8080/ingredients \
  -H"Content-type: application/json" \
  -d'{"id":"SHMP", "name":"Coconut Shrimp", "type":"PROTEIN"}' \
  -H"Authorization: Bearer $token"

这一次应该会创建出新的配料。您可以使用 curl 或 HTTP 客户端以执行对接口的 GET 请求:

$ curl localhost:8080/ingredients
[
  {
    "id": "FLTO",
    "name": "Flour Tortilla",
    "type": "WRAP"
  },

  ...

  {
    "id": "SHMP",
    "name": "Coconut Shrimp",
    "type": "PROTEIN"
  }
]

“椰子虾”配料,现在被列在从该工厂返回的所有配料清单的末尾。新建确实成功了!

我们知道访问令牌 5 分钟后就会过期。如果令牌过期,请求会再次返回 HTTP 401 响应码。您可以使用与访问令牌一起使用的刷新令牌,通过向授权服务器发出请求来获取新的访问令牌(将实际刷新令牌替换为“$refreshToken”):

$ curl localhost:9000/oauth2/token \
  -H"Content-type: application/x-www-form-urlencoded" \
  -d"grant_type=refresh_token&refresh_token=$refreshToken" \
  -u taco-admin-client:secret

有了一个新创建的访问令牌,您可以继续为随心所欲的创建新的配料了。

既然我们已经保护了 /ingredients 接口,那么就应该对其他 API 也使用相同的技术来进行保护了。例如,/orders 接口不应该让任何类型的请求打开,即使是 HTTP GET 请求,因为这将允许黑客轻松获取客户信息。我让您自己来处理对其他 API 接口的安全保护工作。

使用 curl 管理 tacocloud 应用程序只适合简单修补,以了解 OAuth 2 令牌是如何在资源保护方面发挥作用的。但最终我们想要的是一个可用于管理配料的真实客户端应用程序。现在我们来关注一下如何创建 OAuth 客户端,该客户端将获取访问令牌并向 API 发出请求。

results matching ""

    No results matching ""