使用RAML在Anypoint平台实现JWT策略执行与API安全

本文详细介绍了如何在Anypoint平台的API Manager中使用RAML项目实施JWT策略执行。内容包括JWT认证流程、RAML安全方案定义、数据类型设计,以及如何集成Auth0进行端点安全保护,同时实现IP白名单和速率限制策略。

JWT策略执行、IP白名单和速率限制:使用Mulesoft、API安全和Cloudhub 2.0

本教程全面介绍了如何在API Manager中使用基于RAML的示例项目实施JWT策略执行。这在通过Anypoint平台的API Manager应用策略时特别有用。在此过程中,您还将学习如何使用第三方认证提供商(如Auth0)保护特定的API端点。

在本项目中,应用了以下策略以增强API安全性和流量控制:

  • JWT策略执行
  • IP白名单
  • 速率限制

先决条件

  • Anypoint Studio 7.21(默认兼容Java 17)
  • Java 17
  • Anypoint Platform账户
  • SOAPUI + POSTMAN
  • Maven

观看视频教程以全面了解概念。

参考下图理解JWT(JSON Web Token)认证过程的工作原理。

  1. 客户端首先调用认证提供商,通过发送客户端ID、客户端密钥和grant_type获取令牌。
  2. 一旦收到令牌,客户端使用它调用实际的资源服务器端点。
  3. 资源服务器通过调用认证提供商来验证令牌。
  4. 如果令牌有效,向客户端返回200 OK响应。
  5. 如果令牌无效,返回错误响应。

下面包含了相关的RAML文件详情供参考。

安全方案RAML文件

jwt-token.raml

此文件定义了基于JWT的安全方案,指定了应如何传递和验证令牌。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#%RAML 1.0 SecurityScheme
type: x-jwt
description: |
      This API uses JWT for authentication. Pass the token in the Authorization header as "Bearer {token}".
describedBy:
      headers:
        Authorization:
          description: |
            JWT access token in the format "Bearer {token}".
          type: string
          required: true
          example: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
      responses:
        401:
          description: |
            Unauthorized. JWT is missing, invalid, or expired.

oauth2.raml

此文件定义了OAuth 2.0安全方案,客户端必须获取访问令牌并在Authorization头中以"Bearer {token}“格式包含它。它支持常见的授权类型,如authorization_code和client_credentials,以及范围访问控制。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#%RAML 1.0 SecurityScheme
type: OAuth 2.0
description: |
      This API is secured using OAuth 2.0. Obtain an access token and include it in the Authorization header as "Bearer {token".
describedBy:
      headers:
        Authorization:
          description: |
            Used to send the OAuth 2.0 access token. Format: "Bearer {token}".
          type: string
          required: true
          example: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
      responses:
        401:
          description: |
            Unauthorized. Access token is missing, invalid, or expired.
        403:
          description: |
            Forbidden. The access token does not have the required scopes or permissions.
settings:
      authorizationUri: https://auth.example.com/oauth2/authorize
      accessTokenUri: https://auth.example.com/oauth2/token
      authorizationGrants: [ authorization_code, client_credentials ]
      scopes: [ read, write, admin ]

basic-auth-details.raml

此文件定义了基本认证方案,使用在Authorization头中传递的标准用户名和密码。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#%RAML 1.0 SecurityScheme
type: Basic Authentication
description: |
      Basic Authentication using username and password.
describedBy:
      headers:
        Authorization:
          description: |
            Standard HTTP Basic Authentication header. Value must be "Basic base64(username:password)".
          type: string
          required: true
      responses:
        401:
          description: Unauthorized. Invalid or missing credentials.

特征文件

特征文件定义如下:

 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
28
29
30
31
32
33
34
35
36
37
#%RAML 1.0 Trait
usage: Apply to collection GET endpoints to support pagination.
queryParameters:
  page:
    type: integer
    required: false
    minimum: 1
    default: 1
    description: The page number to retrieve (starting from 1).
    example: 2
  pageSize:
    type: integer
    required: false
    minimum: 1
    maximum: 100
    default: 20
    description: The number of items per page.
    example: 10
responses:
  200:
    headers:
      X-Total-Count:
        type: integer
        description: Total number of items available.
        example: 57
      X-Total-Pages:
        type: integer
        description: Total number of pages available.
        example: 6
      X-Current-Page:
        type: integer
        description: The current page number.
        example: 2
      X-Page-Size:
        type: integer
        description: The number of items per page in this response.
        example: 10

类型RAML文件

以下RAML文件定义了API中使用的数据类型:

Booking.raml

定义Booking数据类型,包括id、hotelId、guestName、checkInDate、checkOutDate、rooms和status等字段。它代表一个完整的酒店预订,包含验证和示例数据。

 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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#%RAML 1.0 DataType
type: object
properties:
      id:
        type: integer
        description: Unique booking identifier
        example: 1001
      hotelId:
        type: integer
        description: ID of the booked hotel
        example: 101
      guestName:
        type: string
        required: true
        description: Name of the guest
        example: "Alice Smith"
      checkInDate:
        type: date-only
        required: true
        description: Check-in date
        example: 2024-07-01
      checkOutDate:
        type: date-only
        required: true
        description: Check-out date
        example: 2024-07-05
      rooms:
        type: integer
        required: true
        minimum: 1
        description: Number of rooms booked
        example: 2
      status:
        type: string
        enum: [ confirmed, cancelled, pending ]
        description: Booking status
        example: confirmed
example:
      id: 1001
      hotelId: 101
      guestName: "Alice Smith"
      checkInDate: 2024-07-01
      checkOutDate: 2024-07-05
      rooms: 2
      status: confirmed

BookingInput.raml

概述了提交酒店预订请求所需的数据格式。它包括hotelId、guestName、checkInDate、checkOutDate和rooms等必填字段,每个字段都有验证规则。此文件排除了通常由系统设置的字段,如id和status。

 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
#%RAML 1.0 DataType
 type: object
 properties:
      hotelId:
        type: integer
        required: true
      guestName:
        type: string
        required: true
      checkInDate:
        type: date-only
        required: true
      checkOutDate:
        type: date-only
        required: true
      rooms:
        type: integer
        required: true
        minimum: 1
 example:
      hotelId: 101
      guestName: "Alice Smith"
      checkInDate: 2024-07-01
      checkOutDate: 2024-07-05
      rooms: 2

ErrorResponse.raml

定义了一个标准的错误响应对象,包含一个必需的属性:message。此消息提供有关遇到的错误的详细信息,例如缺少资源、无效输入或认证失败。

1
2
3
4
5
6
7
8
9
#%RAML 1.0 DataType
  type: object
  properties:
      message:
        type: string
        required: true
        description: Error message
  example:
      message: "Resource not found."

Hotel.raml

表示API响应中使用的完整Hotel数据类型。它包括以下属性:

  • id: 酒店的唯一标识符
  • name: 酒店名称(必需)
  • address: 酒店地址(必需)
  • rating: 可选的浮点值(1-5),表示酒店的评分
  • amenities: 可用功能的可选列表(例如"wifi”、“pool”)
 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
28
29
30
31
32
33
34
35
36
37
#%RAML 1.0 DataType
type: object
description: Data type for hotel
properties:
      id:
        type: integer
        description: Unique hotel identifier
        example: 101
      name:
        type: string
        required: true
        description: Name of the hotel
        example: "Grand Plaza"
      address:
        type: string
        required: true
        description: Address of the hotel
        example: "123 Main St, Cityville"
      rating?:
        type: number
        format: float
        minimum: 1
        maximum: 5
        description: Hotel rating (1-5 stars)
        example: 4.5
      amenities?:
        type: array
        items: string
        description: List of amenities
        example: [ "wifi", "pool", "gym" ]
example:
      id: 101
      name: "Grand Plaza"
      address: "123 Main St, Cityville"
      rating: 4.5
      amenities: [ "wifi", "pool", "gym" ]
additionalProperties: false

HotelInput.raml

指定提交新酒店预订的格式和必需详细信息。它包括:

  • name(字符串,必需):酒店名称
  • address(字符串,必需):酒店地址
  • rating(数字,可选):酒店评分,从1到5(浮点数)
  • amenities(字符串数组,可选):酒店设施列表,如"wifi"、“pool”、“gym”

此类型在POST或PUT请求中发送酒店详细信息时使用。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#%RAML 1.0 DataType
type: object
properties:
      name:
        type: string
        required: true
      address:
        type: string
        required: true
      rating?:
        type: number
        format: float
        minimum: 1
        maximum: 5
      amenities?:
        type: array
        items: string
example:
      name: "Grand Plaza"
      address: "123 Main St, Cityville"
      rating: 4.5
      amenities: [ "wifi", "pool", "gym" ]
additionalProperties: false

order-api-security.raml

此RAML文件定义了包含安全和数据类型的酒店预订API:

  • 标题:order-api-security
  • 描述:用于酒店预订管理的API
  • 媒体类型:application/json
  • 协议:HTTP
  • 版本:v1
  • 基础URI:http://api.example.com/hotel-booking/v1

安全方案:

  • 基本认证
  • OAuth 2.0
  • JWT令牌

特征:

  • 应用于GET集合的分页支持

包含的数据类型:

  • Hotel
  • HotelInput
  • Booking
  • BookingInput
  • ErrorResponse

端点:

  • /hotels 用于列出和创建酒店(GET支持分页)
  • /hotels/{hotelId} 用于检索、更新和删除特定酒店

API由basicAuth、oAuth2和jwtToken方案保护。

  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
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#%RAML 1.0
title: order-api-security
description: "This api contain the hotel booking details"
mediaType: 
- application/json
protocols:
  - HTTP
version: v1
baseUri: http://api.example.com/hotel-booking/v1
securitySchemes:
  basicAuth: !include AUTH/basic-auth-details.raml
  oAuth2: !include AUTH/oauth2.raml
  jwtToken: !include AUTH/jwt-token.raml
traits:
  paginated: !include TRAIT/trait.raml

types:
  Hotel: !include TYPE/Hotel.raml
  HotelInput: !include TYPE/HotelInput.raml
  Booking: !include TYPE/Booking.raml
  BookingInput: !include TYPE/BookingInput.raml
  ErrorResponse: !include TYPE/ErrorResponse.raml

securedBy: [ basicAuth, oAuth2,jwtToken]

/hotels:
  get:
    description: Get a paginated list of hotels.
    is: [ paginated ]
    responses:
      200:
        description: Successful response with a list of hotels.
        body:
          application/json:
            type: Hotel[]
            example:
              - id: 101
                name: "Grand Plaza"
                address: "123 Main St, Cityville"
                rating: 4.5
                amenities: [ "wifi", "pool", "gym" ]
              - id: 102
                name: "Ocean View"
                address: "456 Beach Rd, Seaside"
                rating: 4.0
                amenities: [ "wifi", "spa" ]
  post:
    description: Create a new hotel.
    body:
      application/json:
        type: HotelInput
        example:
          name: "Grand Plaza"
          address: "123 Main St, Cityville"
          rating: 4.5
          amenities: [ "wifi", "pool", "gym" ]
    responses:
      201:
        description: Hotel created successfully.
        body:
          application/json:
            type: Hotel
            example:
              id: 103
              name: "Mountain Retreat"
              address: "789 Hilltop Ave, Mountainview"
              rating: 5
              amenities: [ "wifi", "sauna" ]

/hotels/{hotelId}:
  uriParameters:
    hotelId:
      type: integer
      required: true
      description: Unique hotel identifier
      example: 101
  get:
    description: Get details of a specific hotel.
    responses:
      200:
        description: Successful response with hotel details.
        body:
          application/json:
            type: Hotel
            example:
              id: 101
              name: "Grand Plaza"
              address: "123 Main St, Cityville"
              rating: 4.5
              amenities: [ "wifi", "pool", "gym" ]
  put:
    description: Update details of a specific hotel
    body:
      application/json:
        type: HotelInput
        example:
          name: "Grand Plaza Updated"
          address: "123 Main St, Cityville"
          rating: 4.7
          amenities: [ "wifi", "pool", "gym", "spa" ]
    responses:
      200:
        description: Hotel updated successfully.
        body:
          application/json:
            type: Hotel
            example:
              id: 101
              name: "Grand Plaza Updated"
              address: "123 Main St, Cityville"
              rating: 4.7
              amenities: [ "wifi", "pool", "gym", "spa" ]
  delete:
    description: Delete a specific hotel.
    responses:
      204:
        description: Hotel deleted successfully. No content.

现在,将您的API发布到Exchange。

要在本地实施项目,请选择如下所示的脚手架选项。

对于实施和自动发现,我假设您已经熟悉,因此这里不涵盖详细信息。

一切就绪后,将项目部署到CloudHub 2.0,它应该显示为正在运行。

现在,登录或注册Auth0提供商以获取JWKS URL。

导航至Auth0 > 我的配置文件 > 应用程序 > 设置 > 端点。

复制JWKS URL并将其粘贴到API Manager中的JWT策略配置中,如下所示。

下一步是获取令牌。您需要以下四条信息来请求它。(注意:此处显示的客户端密钥已修改以确保安全。)

1
2
3
4
5
6
{
    "client_id":"j5WYzDUApVpIT9QbXQ6IHHXDcAHYrvP1",
    "client_secret":"hnQmxxsxsxsxsxsoL73qxqOmX1fDV37SN4wYjEMhhkj05d-xlR4oeKExY8PFDYm39iL",
    "audience":"https://dev-0qcnlhjb6rx4ckcw.us.auth0.com/api/v2/",
    "grant_type":"client_credentials"
}

接下来,使用令牌调用实际的API。如果令牌有效,您将收到预期的响应。

如果令牌无效,您将收到401 Unauthorized响应。

IP允许列表策略和速率限制很简单——请参考视频获取详细解释。

如果您觉得本教程有帮助,请点赞并随时提出任何问题!

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计