typescript - 在 CDK 中向 api 资源添加身份验证的问题
问题描述
当我尝试向 API 资源添加身份验证时,我收到以下错误消息:
3:26:26 PM | CREATE_FAILED | AWS::ApiGateway::Authorizer |
AccountApi/accountAuthorizer
Resource handler returned message: "Unable to complete operation due to concurrent modification. Please try again later. (Service: ApiGateway, Status Code: 409, Reques
t ID: 4bc8bc51-8ebe-4177-924a-42af2342df27, Extended Request ID: null)" (RequestToken: f6c9ee05-2034-6058-4a07-96a4da3d1aeb, HandlerErrorCode: AlreadyExists)
3:26:28 PM | ROLLBACK_IN_PROGRESS | AWS::CloudFormation::Stack | GrayhoundBackendAwsStack
The following resource(s) failed to create: [AccountApiAccount43EED5FE, AccountApiaccountcreateAccountServiceRoleB6D80C8B, AccountApiaccountgetAccountsServiceRole03782
259, AccountApiaccountAuthorizer2D615F6B, AccountApiapiCloudWatchRole5D5D2C95]. Rollback requested by user.
3:26:28 PM | ROLLBACK_IN_PROGRESS | AWS::CloudFormation::Stack | GrayhoundBackendAwsStack
The following resource(s) failed to create: [AccountApiAccount43EED5FE, AccountApiaccountcreateAccountServiceRoleB6D80C8B, AccountApiaccountgetAccountsServiceRole03782
259, AccountApiaccountAuthorizer2D615F6B, AccountApiapiCloudWatchRole5D5D2C95]. Rollback requested by user.
错误发生在 Authorizer 创建函数中:
const authorizer = this.userPool.createAuthorizer({
parent: this,
name: "accountAuthorizer",
restApiId: apiGatewayResource.resourceId
})
我正在关注这个示例(AWS cdk example/typescript/cognito-api-lambdaLambdaRestApi
,但我没有使用,
而是RestApi
将授权人代码移动到构造中。所以在顶层我有:
import * as cdk from '@aws-cdk/core';
import { Account } from './api';
import { UserPoolConstruct } from './user-pool'
export class GrayhoundBackendAwsStack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const userPool = new UserPoolConstruct(this, 'UserPool', {})
const account = new Account(this, 'AccountApi', { userPool })
}
}
UserPool 构造是:
import * as cognito from '@aws-cdk/aws-cognito';
import * as api from '@aws-cdk/aws-apigateway';
import * as cdk from '@aws-cdk/core';
export class UserPoolConstruct extends cdk.Construct {
userPool: cognito.UserPool
constructor(scope: cdk.Construct, id: string, props?: any) {
super(scope, id);
// User Pool
this.userPool = new cognito.UserPool(this, 'userpool', {
userPoolName: 'grayhound-userpool',
selfSignUpEnabled: true,
signInAliases: {
email: true,
},
autoVerify: {
email: true,
},
standardAttributes: {
givenName: {
required: true,
mutable: true,
},
familyName: {
required: true,
mutable: true,
},
},
customAttributes: {
country: new cognito.StringAttribute({ mutable: true }),
city: new cognito.StringAttribute({ mutable: true }),
isAdmin: new cognito.StringAttribute({ mutable: true }),
},
passwordPolicy: {
minLength: 6,
requireLowercase: true,
requireDigits: true,
requireUppercase: false,
requireSymbols: false,
},
accountRecovery: cognito.AccountRecovery.EMAIL_ONLY,
removalPolicy: cdk.RemovalPolicy.DESTROY,
});
// User Pool Client attributes
const standardCognitoAttributes = {
givenName: true,
familyName: true,
email: true,
emailVerified: true,
address: true,
birthdate: true,
gender: true,
locale: true,
middleName: true,
fullname: true,
nickname: true,
phoneNumber: true,
phoneNumberVerified: true,
profilePicture: true,
preferredUsername: true,
profilePage: true,
timezone: true,
lastUpdateTime: true,
website: true,
};
const clientReadAttributes = new cognito.ClientAttributes()
.withStandardAttributes(standardCognitoAttributes)
.withCustomAttributes(...['country', 'city', 'isAdmin']);
const clientWriteAttributes = new cognito.ClientAttributes()
.withStandardAttributes({
...standardCognitoAttributes,
emailVerified: false,
phoneNumberVerified: false,
})
.withCustomAttributes(...['country', 'city']);
// // User Pool Client
const userPoolClient = new cognito.UserPoolClient(this, 'userpool-client', {
userPool: this.userPool,
authFlows: {
adminUserPassword: true,
custom: true,
userSrp: true,
},
supportedIdentityProviders: [
cognito.UserPoolClientIdentityProvider.COGNITO,
],
readAttributes: clientReadAttributes,
writeAttributes: clientWriteAttributes,
});
// Outputs
new cdk.CfnOutput(this, 'userPoolId', {
value: this.userPool.userPoolId,
});
new cdk.CfnOutput(this, 'userPoolClientId', {
value: userPoolClient.userPoolClientId,
});
}
createAuthorizer({
parent,
name,
restApiId,
} : {
parent: cdk.Construct,
name: string,
restApiId: string,
}) {
const authorizer = new api.CfnAuthorizer(parent, name, {
restApiId: restApiId,
name: `${name}-${restApiId}`,
type: 'COGNITO_USER_POOLS',
identitySource: 'method.request.header.Authorization',
providerArns: [this.userPool.userPoolArn],
})
return authorizer
}
}
帐户结构是:
import * as cdk from '@aws-cdk/core';
import * as lambda from '@aws-cdk/aws-lambda';
import * as dynamodb from '@aws-cdk/aws-dynamodb';
import * as api from '@aws-cdk/aws-apigateway';
import { getAwsSdkv3Layer, getDbModelsLayer, getApiGateway } from '../components'
import { UserPoolConstruct } from '../user-pool';
export interface IAccountConstructProps {
userPool: UserPoolConstruct
}
export class Account extends cdk.Construct {
/** allows accessing the counter function */
public readonly table: dynamodb.Table;
private readonly userPool: UserPoolConstruct
constructor(scope: cdk.Construct, id: string, props: IAccountConstructProps) {
super(scope, id);
this.userPool = props.userPool
this.table = new dynamodb.Table(this, 'Account', {
tableName: 'Account',
partitionKey: {
name: 'id',
type: dynamodb.AttributeType.STRING
},
billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
removalPolicy: cdk.RemovalPolicy.DESTROY,
});
// get the lambda layers, these are simple lambda layers
const awsSdkV3Layer = getAwsSdkv3Layer(this)
const dbModelLayer = getDbModelsLayer(this)
// add the layers to all the lambda functions
// and set the runtime for all lambdas
const lambdaParams = {
runtime: lambda.Runtime.NODEJS_14_X,
code: lambda.Code.fromAsset('src/lambda/api'),
layers: [awsSdkV3Layer, dbModelLayer],
environment: {
INVENTORY_TABLE_NAME: this.table.tableName
},
}
// create the lamdva layers, some code in the source folder...
const getAccountsLambda = new lambda.Function(this, 'account.getAccounts', {
handler: 'account.getAccounts',
...lambdaParams
});
const createAccountLambda = new lambda.Function(this, 'account.createAccount', {
handler: 'account.createAccount',
...lambdaParams
});
// allow the lambda function to access the dynamoDb table
this.table.grantReadWriteData(getAccountsLambda);
this.table.grantReadWriteData(createAccountLambda);
// create the RestApi, see code bellow
const apiGatewayResource = getApiGateway(this)
const accountRes = apiGatewayResource.addResource('account');
let getAccountApi = accountRes.addMethod('GET',
new api.LambdaIntegration(getAccountsLambda, {
requestTemplates: { "application/json": '{ "statusCode": "200" }' }
})
);
// specify the cognito authorizer, HERE is where the error happens
const authorizer = this.userPool.createAuthorizer({
parent: this,
name: "accountAuthorizer",
restApiId: apiGatewayResource.resourceId
})
// // Get underlying post_method Resource object. Returns CfnMethod
// let post_method_resource = getAccountApi.node.findChild('Resource') as api.CfnMethod;
// // Add properties to low level resource
// post_method_resource.addPropertyOverride('AuthorizationType', 'COGNITO_USER_POOLS')
// // AuthorizedId uses Ref, simulate with a dictionaty
// post_method_resource.addPropertyOverride('AuthorizerId', { Ref: authorizer.logicalId })
accountRes.addMethod('POST',
new api.LambdaIntegration(createAccountLambda, {
requestTemplates: { "application/json": '{ "statusCode": "200" }' }
})
);
}
}
这是getApiGateway
代码:
let apiResource: apigateway.Resource | null = null
export const getApiGateway = (parent: cdk.Construct) => {
if(!apiResource) {
const api = new apigateway.RestApi(parent, "api", {
restApiName: "Account Rest API",
description: "This service manages accounts"
});
apiResource = api.root.addResource('api');
}
return apiResource
}
解决方案
推荐阅读
- python - 如何使用 python 在折线图中显示超过 50% 准确度的标签?
- flutter - 颤振 | Riverpod 和 Dart 未处理的异常:在构建期间调用了 setState() 或 markNeedsBuild()
- php - 通过 isset 和 POST 使用变量
- python - 给定该列中值的已排序列表,如何对一列中的熊猫数据框进行排序?
- python-3.x - 在等待多个“wait_for(reaction_add)”被调用时,如何修复我的不和谐机器人?
- r - 如何在每个月初安排一次taskscheduleR?
- python - 使用 MRJob 向所有 Hadoop 映射器广播文件内容
- ruby-on-rails - Rails 6将变量传递给部分
- javascript - 如何使 Lodash sortBy() 对数据进行降序排序?
- postman - 基于没有子任务的用户故事过滤 Jira API 调用