控制在 API 网关中调用 Rest API 的访问权限
Control access for invoking Rest API in API Gateway
我有几个 API 网关资源,我想让其他服务调用它们。
假设我有这两个端点:
- /任务
- /设置
我的客户是一些服务,他们调用这些 Rest APIs 就像下面的请求:(它是用 Javascript 写的,但他们可以使用任何其他编程语言,但不能使用 AWS SDK)
fetch('.../tasks')
.then((tasks) => {
console.log('Tasks:', tasks)
});
我需要检查客户的权限,因为他们打电话给我 API。当服务向 /tasks 发送请求时,我应该检查它的权限,看看它是否没有所需的权限,我将 return 403 作为响应。
我想知道实现它的最佳方法是什么?我应该使用与身份池集成的 AWS Cognito 用户池还是自定义授权方?
如果我的问题不够清楚,请评论,我会提供更多信息。
希望有相关经验的朋友能帮帮我。
API 用户在 API 请求中包含 IAM 用户的访问密钥。 Details.
您需要选择适合您业务需求的身份提供者。
现在分享我的发现已经晚了,但我会把这个方法写下来,供其他对 AWS 身份验证和授权有同样问题的人使用。
首先请注意以下需求无法通过AWS Cognito实现。换句话说,我们必须使用 AWS SDK(据我了解)来获取 AWS 凭证。
My clients are some services, they invoke these Rest APIs like the following request: (It is written in Javascript, but they can use any other programming languages, but can't use AWS SDK)
让我们看一下基于 AWS 文档的 AWS Cognito 及其组件的描述:
Amazon Cognito provides authentication, authorization, and user
management for your web and mobile apps. Your users can sign in
directly with a user name and password, or through a third party such
as Facebook, Amazon, Google or Apple.
The two main components of Amazon Cognito are user pools and identity
pools. User pools are user directories that provide sign-up and
sign-in options for your app users. Identity pools enable you to grant
your users access to other AWS services.
所以只是为了澄清上面的解释,我们可以使用用户池组件提供身份验证和身份池来提供对 AWS 服务的访问和最终授权。
请注意,如果不使用身份池,AWS 不支持授权。
由于有一篇关于如何实施身份验证和授权的信息性文章 - 链接如下 - 我只是浏览了相关场景的摘要,我发现在执行实施步骤之前了解这些内容很有用。
http://interworks.com.mk/amazon-cognito-and-api-gateway-aws-iam-authorization/
用户应使用用户池对自己进行身份验证。您的应用程序用户可以直接通过用户池登录,也可以通过 third-party 身份提供商 (IdP) 联合。 sign-in 过程可以通过网络或 API.
进行
身份验证成功后,您的 Web 或移动应用程序将从 Amazon Cognito 接收身份验证用户的用户池令牌。
您应该使用这些令牌来检索允许您的应用程序访问其他 AWS 服务的 AWS 凭证。
您可以使用收到的凭据发送请求以访问 AWS 服务,在我们的示例中是 API 网关。向 AWS API 网关发送请求有两种选择。第一个是使用 AWS SDK,第二个是使用其他库,如 Axios。关键是如果您手动发送请求(例如通过 axios),您应该在发送之前签署请求。但是如果你使用AWS SDK,AWS SDK会自己处理签名过程,你不需要做任何事情。
有一个在 NodeJs 中实现此场景的示例代码。在此示例中,我们使用 Axios 库将请求发送到 API 网关。
const AWS = require("aws-sdk");
const crypto = require("crypto");
const aws4 = require("aws4");
const axios = require("axios");
// Client information
const CLIENT_SECRET = "######";
const CLIENT_ID = "####";
const USER_POOL_ID = "####";
const IDENTITY_POOL_ID = "####";
const COGNITO_AUTH_PROVIDER =
"cognito-idp.<REGION>.amazonaws.com/<USER_POOL_ID>";
// AWS config
AWS.config.region = "XXXX";
// If client_secret is enabled in user pool, client_secret should be converted to a Base64 encoded value called HASH_SECRET
const createHashSecret = (username) => {
return crypto
.createHmac("SHA256", CLIENT_SECRET)
.update(`${username}${CLIENT_ID}`)
.digest("base64");
};
// User information
const userInfo = {
username: "###",
password: "###",
};
const userPoolData = {
UserPoolId: USER_POOL_ID,
ClientId: CLIENT_ID,
};
/**
* Add user credential to AWS request sent by HTTP
* By Signature Version 4
*/
const generateApiGatewayRequest = (credentials, requestParams) => {
const { url, hostname, path, method } = requestParams;
const options = {
service: "execute-api",
region: AWS.config.region,
url,
hostname,
path,
method,
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
};
// Sign the request by some metadata
return aws4.sign(options, credentials);
};
// Create an authentication client to connect to user pool
const cognitoIdentityServiceProvider = new AWS.CognitoIdentityServiceProvider(
userPoolData
);
// Request to authenticate a user
cognitoIdentityServiceProvider.initiateAuth({
AuthFlow: "USER_PASSWORD_AUTH",
ClientId: CLIENT_ID,
AuthParameters: {
USERNAME: userInfo.username,
PASSWORD: userInfo.password,
SECRET_HASH: createHashSecret(userInfo.username),
},
}, function (err, data) {
if (err) {
// Something wrong has happened, like invalid user info, invalid pool and client info, ...
// The error object contains message, code, time, requestId, statusCode, ...
console.log(err);
} else {
// id token, access token and refresh token have been created.
console.log(data.AuthenticationResult);
// Request for authorization with Identity pool
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: IDENTITY_POOL_ID,
Logins: {
[COGNITO_AUTH_PROVIDER]: data.AuthenticationResult.IdToken,
},
});
// Check if the request for AWS credential has been done successfully
AWS.config.credentials.get(function (err) {
if (err) {
// There is no received credential
console.log(err);
} else {
// The logged-in user now has a temporary credential to ask for AWS services such as Api Gateway
// The credential includes accessKeyId, secretAccessKey, sessionToken, etc
var userCredential = AWS.config.credentials;
// Request protected resources (Api Gateway)
axios(
generateApiGatewayRequest(userCredential, {
url:
"https://XXX.execute-api.us-east-1.amazonaws.com/main/tasks",
hostname: "XXX.execute-api.us-east-1.amazonaws.com",
path: "/main/tasks",
method: "GET",
})
)
.then((result) => {
// Request's been done successfully
console.log(result);
})
.catch((error) => {
// The error could be either resource errors like Bad Request or Forbidden as the user doesn't have the permission to access the resource
console.log(error);
});
}
});
}
});
有关详细信息,请参阅上面链接的文章。不过,如果还有不明白的地方,欢迎在下方留言。
我有几个 API 网关资源,我想让其他服务调用它们。 假设我有这两个端点:
- /任务
- /设置
我的客户是一些服务,他们调用这些 Rest APIs 就像下面的请求:(它是用 Javascript 写的,但他们可以使用任何其他编程语言,但不能使用 AWS SDK)
fetch('.../tasks')
.then((tasks) => {
console.log('Tasks:', tasks)
});
我需要检查客户的权限,因为他们打电话给我 API。当服务向 /tasks 发送请求时,我应该检查它的权限,看看它是否没有所需的权限,我将 return 403 作为响应。
我想知道实现它的最佳方法是什么?我应该使用与身份池集成的 AWS Cognito 用户池还是自定义授权方?
如果我的问题不够清楚,请评论,我会提供更多信息。
希望有相关经验的朋友能帮帮我。
API 用户在 API 请求中包含 IAM 用户的访问密钥。 Details.
您需要选择适合您业务需求的身份提供者。
现在分享我的发现已经晚了,但我会把这个方法写下来,供其他对 AWS 身份验证和授权有同样问题的人使用。
首先请注意以下需求无法通过AWS Cognito实现。换句话说,我们必须使用 AWS SDK(据我了解)来获取 AWS 凭证。
My clients are some services, they invoke these Rest APIs like the following request: (It is written in Javascript, but they can use any other programming languages, but can't use AWS SDK)
让我们看一下基于 AWS 文档的 AWS Cognito 及其组件的描述:
Amazon Cognito provides authentication, authorization, and user management for your web and mobile apps. Your users can sign in directly with a user name and password, or through a third party such as Facebook, Amazon, Google or Apple.
The two main components of Amazon Cognito are user pools and identity pools. User pools are user directories that provide sign-up and sign-in options for your app users. Identity pools enable you to grant your users access to other AWS services.
所以只是为了澄清上面的解释,我们可以使用用户池组件提供身份验证和身份池来提供对 AWS 服务的访问和最终授权。
请注意,如果不使用身份池,AWS 不支持授权。
由于有一篇关于如何实施身份验证和授权的信息性文章 - 链接如下 - 我只是浏览了相关场景的摘要,我发现在执行实施步骤之前了解这些内容很有用。
http://interworks.com.mk/amazon-cognito-and-api-gateway-aws-iam-authorization/
用户应使用用户池对自己进行身份验证。您的应用程序用户可以直接通过用户池登录,也可以通过 third-party 身份提供商 (IdP) 联合。 sign-in 过程可以通过网络或 API.
进行身份验证成功后,您的 Web 或移动应用程序将从 Amazon Cognito 接收身份验证用户的用户池令牌。
您应该使用这些令牌来检索允许您的应用程序访问其他 AWS 服务的 AWS 凭证。
您可以使用收到的凭据发送请求以访问 AWS 服务,在我们的示例中是 API 网关。向 AWS API 网关发送请求有两种选择。第一个是使用 AWS SDK,第二个是使用其他库,如 Axios。关键是如果您手动发送请求(例如通过 axios),您应该在发送之前签署请求。但是如果你使用AWS SDK,AWS SDK会自己处理签名过程,你不需要做任何事情。
有一个在 NodeJs 中实现此场景的示例代码。在此示例中,我们使用 Axios 库将请求发送到 API 网关。
const AWS = require("aws-sdk");
const crypto = require("crypto");
const aws4 = require("aws4");
const axios = require("axios");
// Client information
const CLIENT_SECRET = "######";
const CLIENT_ID = "####";
const USER_POOL_ID = "####";
const IDENTITY_POOL_ID = "####";
const COGNITO_AUTH_PROVIDER =
"cognito-idp.<REGION>.amazonaws.com/<USER_POOL_ID>";
// AWS config
AWS.config.region = "XXXX";
// If client_secret is enabled in user pool, client_secret should be converted to a Base64 encoded value called HASH_SECRET
const createHashSecret = (username) => {
return crypto
.createHmac("SHA256", CLIENT_SECRET)
.update(`${username}${CLIENT_ID}`)
.digest("base64");
};
// User information
const userInfo = {
username: "###",
password: "###",
};
const userPoolData = {
UserPoolId: USER_POOL_ID,
ClientId: CLIENT_ID,
};
/**
* Add user credential to AWS request sent by HTTP
* By Signature Version 4
*/
const generateApiGatewayRequest = (credentials, requestParams) => {
const { url, hostname, path, method } = requestParams;
const options = {
service: "execute-api",
region: AWS.config.region,
url,
hostname,
path,
method,
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
};
// Sign the request by some metadata
return aws4.sign(options, credentials);
};
// Create an authentication client to connect to user pool
const cognitoIdentityServiceProvider = new AWS.CognitoIdentityServiceProvider(
userPoolData
);
// Request to authenticate a user
cognitoIdentityServiceProvider.initiateAuth({
AuthFlow: "USER_PASSWORD_AUTH",
ClientId: CLIENT_ID,
AuthParameters: {
USERNAME: userInfo.username,
PASSWORD: userInfo.password,
SECRET_HASH: createHashSecret(userInfo.username),
},
}, function (err, data) {
if (err) {
// Something wrong has happened, like invalid user info, invalid pool and client info, ...
// The error object contains message, code, time, requestId, statusCode, ...
console.log(err);
} else {
// id token, access token and refresh token have been created.
console.log(data.AuthenticationResult);
// Request for authorization with Identity pool
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: IDENTITY_POOL_ID,
Logins: {
[COGNITO_AUTH_PROVIDER]: data.AuthenticationResult.IdToken,
},
});
// Check if the request for AWS credential has been done successfully
AWS.config.credentials.get(function (err) {
if (err) {
// There is no received credential
console.log(err);
} else {
// The logged-in user now has a temporary credential to ask for AWS services such as Api Gateway
// The credential includes accessKeyId, secretAccessKey, sessionToken, etc
var userCredential = AWS.config.credentials;
// Request protected resources (Api Gateway)
axios(
generateApiGatewayRequest(userCredential, {
url:
"https://XXX.execute-api.us-east-1.amazonaws.com/main/tasks",
hostname: "XXX.execute-api.us-east-1.amazonaws.com",
path: "/main/tasks",
method: "GET",
})
)
.then((result) => {
// Request's been done successfully
console.log(result);
})
.catch((error) => {
// The error could be either resource errors like Bad Request or Forbidden as the user doesn't have the permission to access the resource
console.log(error);
});
}
});
}
});
有关详细信息,请参阅上面链接的文章。不过,如果还有不明白的地方,欢迎在下方留言。