求助:基于.NET C#开发AWS Lambda PreSignUp_ExternalProvider触发器同步用户到SQL
实现AWS Cognito PreSignUp_ExternalProvider触发器同步本地SQL用户(.NET C#)
以下是完整的实现方案,包含代码示例和关键配置说明:
1. 依赖准备
在Lambda项目中安装以下NuGet包:
Amazon.Lambda.CoreAmazon.Lambda.CognitoEventsMicrosoft.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
错误处理策略
- 如果数据库同步失败,可选择两种处理方式:
- 阻止注册:抛出异常,Cognito会终止注册流程,用户无法完成认证。
- 允许注册后续补同步:仅记录错误,允许注册继续,后续通过定时任务或其他机制补全本地用户数据。
内容的提问来源于stack exchange,提问作者Lazar Nikolić
相关产品推荐
相关产品推荐

