在此模块中,您将使用您已部署的应用程序。在此之前,我们来快速回顾一下您在应用程序中使用的 AWS 组件:

我们来看看这些组件时如何联系在一起的。在以下步骤中,您将使用不同的组件逐个了解不同的应用程序终端节点。

第一,您先使用 Registration 终端节点,新用户在此终端节点注册并创建他们的角色。

第二,您使用 Login 终端节点,用户可在此终端节点使用客户端(例如 Web 应用程序或移动应用程序)进行身份验证和接收 ID 令牌。

第三,您使用 AddItem 终端节点,向用户的库存添加新项目。

最后,您使用 GetUser 终端节点,检索用户的信息,包括用户的库存中包括的所有项目。

完成模块所需时间:20 分钟


  • 第 1 步:注册新用户

    您查看的第一个工作流程是 Registration 终端节点。在此终端节点,新用户通过提供登录信息(例如用户名和密码)及其角色的属性(例如身高和体重)进行注册。

    当您查看这些终端节点时,终端节点调用的相关代码片段会显示在此指南中。

    只需向 /users 终端节点发出 POST 请求,您的注册终端节点即可使用。您可以在 application/app.js 文件的中间部分看到此终端节点的逻辑:

    // Create user
    app.post('/users', wrapAsync(async (req, res) => {
      const validated = validateCreateUser(req.body)
      if (!validated.valid) {
        throw new Error(validated.message)
      }
      await createCognitoUser(req.body.username, req.body.password, req.body.email)
      const user = await createUser(req.body.username, req.body.height, req.body.weight)
      res.json(user)
    }))

    您的处理程序首先对请求验证传入的负载主体。如果成功,处理程序会在 Amazon Cognito 中创建用户,然后在数据库中创建用户。

    您已在 Amazon Cognito 模块中了解了 createCognitoUser 函数,我们在此不再赘述。我们来看一下 createUser 函数,您可在 application/data/createUser.js 中找到此函数。

    const { executeWriteSql } = require('./utils')
    
    const createUser = async (username, height, weight) => {
      sql = `INSERT INTO users (user_id, username, height, weight) \
    VALUES (DEFAULT, :username, :height, :weight) \
    RETURNING user_id, username, height, weight`
      parameters = [
        {
          name: 'username',
          value: { stringValue: username }
        },
        {
          name: 'height',
          value: { longValue: height}
        },
        {
          name: 'weight',
          value: { longValue: weight}
        }
      ]
      const result = await executeWriteSql(sql, parameters)
      return result[0]
    }
    
    module.exports = createUser

    在此方法中,您编写一些参数化的 SQL 和参数,并将其传递给 executeWriteSql 帮助程序函数。

    尝试调用您的 Registration 终端节点以创建新用户。在终端中运行以下命令:

    curl -X POST ${BASE_URL}/users \
      -H 'Content-Type: application/json' \
      -d '{
    	"username": "bonecrusher",
    	"password": "Mypassword1",
    	"email": "test@hello.com",
    	"height": 75,
    	"weight": 350
    }'

    您应该能在终端中看到以下输出:

    {"user_id":51,"username":"bonecrusher","height":75,"weight":350}

    很好! 您已成功创建用户。现在,我们来登录并接收 ID 令牌。

  • 第 2 步:登录以获取用户凭证

    您的第二个终端节点是 Login 终端节点.用户会将其用户名和密码提交至此终端节点以接收 ID 令牌,该 ID 令牌将用于在之后的请求中对用户进行身份验证。

    要处理此身份验证和令牌分发,您的应用程序有一个 /login 终端节点。此处理程序位于 application/app.js 中,如下所示:

    // Login
    app.post('/login', wrapAsync(async (req, res) => {
      const idToken = await login(req.body.username, req.body.password)
      res.json({ idToken })
    }))

    它要求负载主体具有用户名密码属性。然后会从 auth.js 文件中调用帮助程序的 login 函数。如果登录成功,则会返回用户的 ID 令牌。

    我们来使用您创建的用户进行测试。在终端中运行以下命令:

    curl -X POST ${BASE_URL}/login \
      -H 'Content-Type: application/json' \
      -d '{
    	"username": "bonecrusher",
    	"password": "Mypassword1"
    }'

    您应该能在终端中看到以下输出:

    {"idToken":"eyJraWQiOiJGRWJmUUdVbHYyUmE0bEg5STFQV2V4Z3lxVDdNSGMrRkJJajRDQVNjdWZVPSIsImFsZyI6IlJTMjU2In0.eyJzdWIiOiI2YzNjNDc3ZS0xMzU0LTRlMzktYjNmZS04OTM3ODEwODhhMTciLCJhdWQiOiI2dWMwMW5hZ2p0M3BpZWF0c2tudDBwcnZlZCIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiZXZlbnRfaWQiOiJkZjRhZTgwMi03MDY1LTQzMmQtYmI1ZC0xNjkyODM2ZTRkYWYiLCJ0b2tlbl91c2UiOiJpZCIsImF1dGhfdGltZSI6MTU3MTc0Mzc5NSwiaXNzIjoiaHR0cHM6XC9cL2NvZ25pdG8taWRwLnVzLWVhc3QtMS5hbWF6b25hd3MuY29tXC91cy1lYXN0LTFfajBsSUJXam9qIiwiY29nbml0bzp1c2VybmFtZSI6ImJvbmVjcnVzaGVyIiwiZXhwIjoxNTcxNzQ3Mzk1LCJpYXQiOjE1NzE3NDM3OTUsImVtYWlsIjoidGVzdEBoZWxsby5jb20ifQ.TzAmg3JF1pYH25f9oZHeR7ZN4ELPnvzmX08JWFm3tMc5b7J4Y5KDU8ybKzcmb2gUdwRFrDPz46oGF0huTSwfxXt4oMU9fKOAntdGwCXu0oZmBY7Gixt63Fg9Grfku924XpbpYjwrbYL8j4QqY66Mt_1sLCKRwbuZHEQf5A2miT7_FWjTbLi7PT4ftU7eZW19dgjq3miLA6r9JeQTfYU06_cgap8hAyULK-28rh_PD1ChWDwYlxKAXy5Zcv14Ja0dRCLJiufK4aSEcweVoipPanZrDzXuB8UjLmswvq4paJq0oq6OEnpx1w6k2ru4H9rYQEtXVIbRSqfHOqprl4XNLw"}

    此 ID 令牌用于在之后的请求中对用户进行授权。在您的 Shell 中保存 ID 令牌的值,方法是复制引号中间的令牌的值,然后运行以下命令:

    export ID_TOKEN=<idToken>
  • 第 3 步:向您的用户添加项目

    您的用户已有了身份验证的方法,我们来向用户的库存添加项目。您可以想象这会在用户在游戏中发现地上或宝箱中的物品时发生。在我们的用例中,您可以想象应用程序客户端不知道所添加项目的类型或属性,这些项目在后端应用程序中生成并返回给用户。

    添加项目的终端节点是 POST /users/:username/itemsapplication/app.js 中的处理程序代码如下所示:

    // Add item
    app.post('/users/:username/items', wrapAsync(async (req, res) => {
      const token = await verifyToken(req.header('Authorization'))
      if (token['cognito:username'] != req.params.username) {
        throw new Error('Unauthorized')
      }
      const item = await addItemToUser(req.params.username)
      res.json(item)
    }))

    此代码执行以下两种操作。首先,它会验证授权标头中发送的 ID 令牌。如果此令牌有效,并且令牌中的用户名与 URL 路径中的用户名匹配,则该用户名将被传递到您的数据包中的 addItemToUser 函数中。

    addItemToUser 函数位于 application/data/addItemToUser.js 中,如下所示:

    const { executeWriteSql } = require('./utils')
    
    const types = [
      'WEAPON',
      'ARMOR',
      'CLOTHING',
      'POTION'
    ]
    
    const addItemToUser = async (username) => {
      const type = types[Math.floor(Math.random()*types.length)]
      let properties
      if (type == 'WEAPON') {
        properties = { attack: Math.floor(Math.random() * 10) + 1  }
      } else if (type == 'ARMOR') {
        properties = { defense: Math.floor(Math.random() * 10) + 1  }
      } else if (type == 'CLOTHING') {
        properties = { charisma: Math.floor(Math.random() * 10) + 1  }
      } else if (type == 'POTION') {
        properties = { healing: Math.floor(Math.random() * 10) + 1  }
      }
      writeSql = `INSERT INTO items (item_id, owner_id, type, properties) \
    VALUES (DEFAULT, (SELECT user_id from users where username = :username), :type, :properties) \
    RETURNING item_id, owner_id, type, properties`
      writeParameters = [
        {
          name: 'username',
          value: { stringValue: username }
        },
        {
          name: 'type',
          value: { stringValue: itemType}
        },
        {
          name: 'properties',
          value: { stringValue: JSON.stringify(properties)}
        }
    
      ]
      const writeResult = await executeWriteSql(writeSql, writeParameters)
      return writeResult[0]
    }
    
    module.exports = addItemToUser

    addItemToUser 函数生成随机项目类型和属性,然后更新项目表,以向用户的库存中添加给定的项目。

    我们来试用此终端节点。在终端中运行以下命令:

    curl -X POST ${BASE_URL}/users/bonecrusher/items \
      -H "Authorization: ${ID_TOKEN}"
    

    注意,您将把您的 ID 令牌与请求一起传递到授权头中。

    您应该能在终端中看到以下输出:

    {"item_id":107,"owner_id":51,"type":"CLOTHING","properties":"{\"charisma\":8}"}

    此新项目已添加至您的用户! 注意,由于项目类型和属性是随机的,因此为您打印的属性略有不同。

    再次尝试运行此终端节点,但不使用 ID 令牌。在终端中运行以下命令:

    curl -X POST ${BASE_URL}/users/bonecrusher/items

    因为您的请求不包括令牌,您应看到以下输出:

    {"message":"jwt must be provided"}

    您已使用 Amazon Cognito 令牌来保护终端节点。

  • 第 4 步:获取用户

    在您的最终终端节点中,我们来获取您的用户,包括此用户库存中的所有项目。通过这种方式,您可以确认您的项目已在上一步中成功添加到您的用户。

    用于获取用户的终端节点是 GET /users/:userId,处理程序代码包含在 application/app.js 中,如下所示:

    // Fetch user
    app.get('/users/:userId', wrapAsync(async (req, res) => {
      const user = await fetchUser(req.params.userId)
      res.json(user)
    }))

    此终端节点不是经过身份验证的终端节点,因为如果需要,每个用户都可以看到其他用户的详细信息。

    通过在您的终端中输入以下命令,调用终端节点并获取用户:

    curl -X GET ${BASE_URL}/users/51

    您应该能在终端中看到以下输出:

    {"user_id":51,"username":"bonecrusher","height":75,"weight":350,"items":[{"item_id":107,"owner_id":51,"type":"CLOTHING","properties":"{\"charisma\":8}"}]}

    成功! 您检索了用户,并且该用户具有您在上一步中添加的新项目。


在此模块中,您使用了正在运行的终端节点来查看各个组件是如何协同运行的。第一,您注册了一个新用户,这需要在 Amazon Cognito 用户池中创建一个新用户,并将该用户添加到 Amazon Aurora Serverless 数据库。第二,您使用登录终端节点来获取 ID 令牌,客户端可以使用该令牌对用户进行身份验证。第三,在向用户的库存添加新项目时,您使用此 ID 令牌对用户进行授权。最后,您获取用户详细信息以查看添加的新项目。

在下一个模块中,我们将清理您创建的资源。