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

求助:基于.NET C#开发AWS Lambda PreSignUp_ExternalProvider触发器同步用户到SQL

实现AWS Cognito PreSignUp_ExternalProvider触发器同步本地SQL用户(.NET C#)

以下是完整的实现方案,包含代码示例和关键配置说明:

1. 依赖准备

在Lambda项目中安装以下NuGet包:

  • Amazon.Lambda.Core
  • Amazon.Lambda.CognitoEvents
  • Microsoft.Data.SqlClient(适配.NET Core/.NET 6+,替代旧版System.Data.SqlClient)

2. Lambda触发器代码

using Amazon.Lambda.Core;
using Amazon.Lambda.CognitoEvents;
using Microsoft.Data.SqlClient;
using System;
using System.Threading.Tasks;

[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))]

namespace CognitoPreSignUpSync
{
    public class Function
    {
        private readonly string _dbConnectionString;

        public Function()
        {
            // 从Lambda环境变量读取数据库连接字符串
            _dbConnectionString = Environment.GetEnvironmentVariable("SqlDbConnection");
            if (string.IsNullOrWhiteSpace(_dbConnectionString))
            {
                throw new InvalidOperationException("未配置SqlDbConnection环境变量");
            }
        }

        public async Task<CognitoPreSignUpResponse> FunctionHandler(CognitoPreSignUpEvent input, ILambdaContext context)
        {
            context.Logger.LogInformation($"处理外部用户预注册: Google用户ID = {input.UserName}");

            // 从Cognito事件中提取用户核心属性
            var googleUserId = input.UserName; // Google的sub作为唯一标识
            var email = input.Request.UserAttributes["email"];
            var firstName = input.Request.UserAttributes.TryGetValue("given_name", out var fn) ? fn : string.Empty;
            var lastName = input.Request.UserAttributes.TryGetValue("family_name", out var ln) ? ln : string.Empty;

            try
            {
                // 避免重复创建用户
                if (!await IsUserExists(googleUserId))
                {
                    await CreateLocalUser(googleUserId, email, firstName, lastName);
                    context.Logger.LogInformation($"本地用户记录创建成功: {googleUserId}");
                }
                else
                {
                    context.Logger.LogInformation($"用户已存在于本地数据库: {googleUserId}");
                }
            }
            catch (Exception ex)
            {
                context.Logger.LogError($"同步本地用户失败: {ex.Message}\n{ex.StackTrace}");
                // 可选:如果必须确保本地用户存在才能使用,取消下面的注释以终止注册流程
                // throw;
            }

            // 允许Cognito继续注册流程,可根据需求开启自动确认/验证
            return new CognitoPreSignUpResponse
            {
                AutoConfirmUser = true,
                AutoVerifyEmail = true
            };
        }

        private async Task<bool> IsUserExists(string externalUserId)
        {
            using var conn = new SqlConnection(_dbConnectionString);
            await conn.OpenAsync();

            const string checkQuery = "SELECT COUNT(1) FROM Users WHERE ExternalUserId = @ExternalId";
            using var cmd = new SqlCommand(checkQuery, conn);
            cmd.Parameters.AddWithValue("@ExternalId", externalUserId);

            var count = (int)await cmd.ExecuteScalarAsync();
            return count > 0;
        }

        private async Task CreateLocalUser(string externalUserId, string email, string firstName, string lastName)
        {
            using var conn = new SqlConnection(_dbConnectionString);
            await conn.OpenAsync();

            const string insertQuery = @"INSERT INTO Users (ExternalUserId, Email, FirstName, LastName, CreatedAt)
                                         VALUES (@ExternalId, @Email, @FirstName, @LastName, @CreatedAt)";
            using var cmd = new SqlCommand(insertQuery, conn);
            cmd.Parameters.AddWithValue("@ExternalId", externalUserId);
            cmd.Parameters.AddWithValue("@Email", email);
            cmd.Parameters.AddWithValue("@FirstName", firstName);
            cmd.Parameters.AddWithValue("@LastName", lastName);
            cmd.Parameters.AddWithValue("@CreatedAt", DateTime.UtcNow);

            await cmd.ExecuteNonQueryAsync();
        }
    }
}

3. 关键配置说明

数据库表结构示例

创建本地用户表(可根据业务需求调整字段):

CREATE TABLE Users (
    Id INT IDENTITY(1,1) PRIMARY KEY,
    ExternalUserId VARCHAR(255) NOT NULL UNIQUE, -- 存储Google的sub值
    Email VARCHAR(255) NOT NULL UNIQUE,
    FirstName VARCHAR(100),
    LastName VARCHAR(100),
    CreatedAt DATETIME NOT NULL DEFAULT GETUTCDATE()
);

Lambda环境配置

  • 在Lambda控制台的「配置」>「环境变量」中添加SqlDbConnection,值为你的SQL数据库连接字符串(例如Server=xxx;Database=xxx;User Id=xxx;Password=xxx;)。
  • 如果数据库部署在AWS VPC内,需将Lambda配置到对应VPC的子网和安全组,确保安全组允许Lambda访问数据库的1433端口(SQL Server默认端口)。

Cognito属性映射配置

在Cognito用户池的「身份提供商」>「Google」配置中,确保已映射所需的用户属性:

  • 将Google的email映射到Cognito的email
  • 将Google的given_name映射到Cognito的given_name
  • 将Google的family_name映射到Cognito的family_name

错误处理策略

  • 如果数据库同步失败,可选择两种处理方式:
    1. 阻止注册:抛出异常,Cognito会终止注册流程,用户无法完成认证。
    2. 允许注册后续补同步:仅记录错误,允许注册继续,后续通过定时任务或其他机制补全本地用户数据。

内容的提问来源于stack exchange,提问作者Lazar Nikolić

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 23:22:52