You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Cognito Hosted UI登录后,Http API返回401未授权排查

问题:Cognito登录后重定向到API返回401 Unauthorized

已实现目标

  • 通过带自定义子域名的HTTP API路由,用Lambda提供HTML内容(已完成)
  • 用授权器限制访问(已完成)
  • 使用Cognito的用户管理和登录功能(已完成)

当前异常

登录流程正常完成后,页面会携带code参数重定向到目标地址:https://private.mydomain.com/showMovies?code=e0c9d56c-b55b-4e24-b868-6e74e2c5fad2,但访问该地址时API直接返回{"message":"Unauthorized"},状态码401。

API访问日志

{
    "requestId": "BbPBXgtNliAEM9Q=",
    "ip": "1234568",
    "requestTime": "07/Mar/2023:19:35:36 +0000",
    "httpMethod": "GET",
    "routeKey": "GET /showMovies",
    "status": "401",
    "protocol": "HTTP/1.1",
    "responseLength": "26"
}

排查情况

已参考相关教程,采用AWS CDK实现上述架构,多次核对配置与教程内容未发现差异,但无法定位问题,也不清楚进一步调试的方法,寻求问题原因。

完整CDK代码

import {
  Stack,
  StackProps,
  CfnOutput,
  RemovalPolicy,
  Duration,
} from "aws-cdk-lib";
import { Function, Runtime, Code } from "aws-cdk-lib/aws-lambda";
import { Bucket } from "aws-cdk-lib/aws-s3";
import { BucketDeployment, Source } from "aws-cdk-lib/aws-s3-deployment";
import { Construct } from "constructs";
import { ARecord, HostedZone, RecordTarget } from "aws-cdk-lib/aws-route53";
import {
  Certificate,
  CertificateValidation,
} from "aws-cdk-lib/aws-certificatemanager";
import { GlobalConfig } from "../config/config";
import {
  DomainName,
  HttpApi,
  HttpMethod,
} from "@aws-cdk/aws-apigatewayv2-alpha";
import { ApiGatewayv2DomainProperties } from "aws-cdk-lib/aws-route53-targets";
import { HttpLambdaIntegration } from "@aws-cdk/aws-apigatewayv2-integrations-alpha";
import { AccountRecovery, OAuthScope, UserPool } from "aws-cdk-lib/aws-cognito";
import { HttpJwtAuthorizer } from "@aws-cdk/aws-apigatewayv2-authorizers-alpha";

export class WebPagesStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);

    // 提供公开可访问的静态内容,如css和jquery
    const publicStaticContentBucket = new Bucket(
      this,
      "static-content-bucket",
      {
        publicReadAccess: true,
        removalPolicy: RemovalPolicy.DESTROY,
        autoDeleteObjects: true,
        versioned: false,
        enforceSSL: true,
      }
    );

    new BucketDeployment(this, "static-content-deployment", {
      sources: [Source.asset("static")],
      destinationBucket: publicStaticContentBucket,
    });

    // 获取存储桶URL,用于Lambda内部调用静态内容
    let publicStaticContentBucketUrl = "https://";
    publicStaticContentBucketUrl = publicStaticContentBucketUrl.concat(
      publicStaticContentBucket.bucketRegionalDomainName
    );

    // 返回HTML内容的Lambda
    const showMoviesLambda = new Function(this, "show-movies-lambda", {
      runtime: Runtime.PYTHON_3_9,
      code: Code.fromAsset(GlobalConfig.lambdaDir),
      handler: "movies_show.handler",
      environment: {
        STATIC_CONTENT_BASE_URL: publicStaticContentBucketUrl,
      },
    });

    // 基于现有域名创建子域名
    const subDomainName = "private." + GlobalConfig.domainName;

    const hostedZone = HostedZone.fromLookup(this, "private-hosted-zone", {
      domainName: GlobalConfig.domainName,
    });

    const subPageCertificate = new Certificate(
      this,
      "private-subdomain-certificate",
      {
        domainName: subDomainName,
        validation: CertificateValidation.fromDns(hostedZone),
      }
    );

    const subDomain = new DomainName(this, "private-domain", {
      domainName: subDomainName,
      certificate: subPageCertificate,
    });

    new ARecord(this, "private-subdomain-a-record", {
      recordName: subDomainName,
      zone: hostedZone,
      target: RecordTarget.fromAlias(
        new ApiGatewayv2DomainProperties(
          subDomain.regionalDomainName,
          subDomain.regionalHostedZoneId
        )
      ),
    });

    // 将新子域名映射到HTTP API
    const privateHttpApi = new HttpApi(this, "private-http-api", {
      description: "仅个人使用",
      defaultDomainMapping: {
        domainName: subDomain,
      },
      apiName: "private-http-api",
    });

    privateHttpApi.applyRemovalPolicy(RemovalPolicy.DESTROY);

    // 创建用户池,用户通过AWS控制台创建
    const user_pool = new UserPool(this, "private-user-pool", {
      userPoolName: "private-user-pool",
      selfSignUpEnabled: false,
      accountRecovery: AccountRecovery.NONE,
      autoVerify: { email: true },
      signInAliases: { email: true },
      signInCaseSensitive: false,
      standardAttributes: {
        email: {
          required: true,
          mutable: true,
        },
        preferredUsername: {
          required: false,
          mutable: true,
        },
      },
      passwordPolicy: {
        minLength: 8,
        requireLowercase: true,
        requireUppercase: true,
        requireDigits: true,
        requireSymbols: false,
        tempPasswordValidity: Duration.days(7),
      },
      removalPolicy: RemovalPolicy.DESTROY,
    });

    /* 提前定义路由路径,用于:
       - 登录后重定向
       - Lambda目标路径
       - 绑定授权器
    */
    const routePath = "/showMovies";

    /*
     * 目前几乎所有配置都已开启,待功能正常后移除不必要的配置
     * 特别是callbackUrl是否被使用存疑
     */
    const user_pool_app_client = user_pool.addClient(
      "private-user-pool-app-client",
      {
        accessTokenValidity: Duration.days(1),
        authFlows: {
          adminUserPassword: true,
          custom: true,
          userPassword: true,
          userSrp: true,
        },
        authSessionValidity: Duration.minutes(15),
        disableOAuth: false,
        oAuth: {
          flows: {
            authorizationCodeGrant: true,
            implicitCodeGrant: true,
          },
          callbackUrls: ["https://" + subDomainName + routePath],
          scopes: [
            OAuthScope.COGNITO_ADMIN,
            OAuthScope.EMAIL,
            OAuthScope.OPENID,
            OAuthScope.PHONE,
            OAuthScope.PROFILE,
          ],
        },
        preventUserExistenceErrors: true,
      }
    );

    /*
     * - 仅用于设置登录后的重定向URI?
     * - domainPrefix非必需,但也无负面影响
     */
    const user_pool_domain = user_pool.addDomain("private-user-pool-domain", {
      cognitoDomain: {
        domainPrefix: "private",
      },
    });

    user_pool_domain.signInUrl(user_pool_app_client, {
      redirectUri: "https://" + subDomainName + routePath,
    });

    user_pool_domain.applyRemovalPolicy(RemovalPolicy.DESTROY);

    new CfnOutput(this, "private-user-pool-domain", {
      value: user_pool.userPoolProviderUrl,
    });

    // JWT授权器
    const authorizer = new HttpJwtAuthorizer(
      "private-show_movies-auhtorizer",
      user_pool.userPoolProviderUrl,
      {
        jwtAudience: [user_pool_app_client.userPoolClientId],
      }
    );

    // 绑定路径、方法、Lambda与授权器
    privateHttpApi.addRoutes({
      path: routePath,
      methods: [HttpMethod.GET],
      integration: new HttpLambdaIntegration(
        "show-movies-lambda-integration",
        showMoviesLambda
      ),
      authorizer: authorizer,
    });

    new CfnOutput(this, "static-content-bucket-url-output", {
      value: publicStaticContentBucketUrl,
      description: "静态内容公开URL",
      exportName: "staticContentBucketUrl",
    });
  }
}

内容的提问来源于stack exchange,提问作者evilive

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 20:57:39