ASP .NET Core 8.0 MVC对接Cognito用户池实现自定义登录认证
ASP.NET Core 8.0 MVC 自定义登录页对接Cognito用户池
1. 基础配置
1.1 配置Cognito参数
在appsettings.json中添加Cognito相关配置:
"Authentication": { "Cognito": { "ClientId": "你的Cognito客户端ID", "ClientSecret": "你的Cognito客户端密钥", "Authority": "https://cognito-idp.你的区域.amazonaws.com/你的用户池ID", "CallbackPath": "/auth/callback", "LogoutPath": "/auth/logout", "SignedOutCallbackPath": "/" } }
1.2 注册认证服务
在Program.cs中配置OpenID Connect认证,禁用自动跳转Cognito内置登录页:
builder.Services.AddAuthentication(options => { options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; }) .AddCookie() .AddOpenIdConnect(options => { var cognitoSettings = builder.Configuration.GetSection("Authentication:Cognito").Get<CognitoSettings>(); options.ClientId = cognitoSettings.ClientId; options.ClientSecret = cognitoSettings.ClientSecret; options.Authority = cognitoSettings.Authority; options.CallbackPath = cognitoSettings.CallbackPath; options.SignedOutCallbackPath = cognitoSettings.SignedOutCallbackPath; options.SaveTokens = true; options.ResponseType = "code"; options.Scope.Add("openid"); options.Scope.Add("profile"); options.Scope.Add("email"); // 拦截自动跳转,改用自定义登录页 options.Events.OnRedirectToIdentityProvider = context => { if (context.Request.Path.StartsWithSegments("/auth/login")) { context.Response.Redirect("/auth/login"); context.HandleResponse(); } return Task.CompletedTask; }; }); app.UseAuthentication(); app.UseAuthorization();
需创建CognitoSettings类绑定配置参数
2. 调整Auth控制器逻辑
替换原有控制器代码,实现自定义登录提交与认证流程:
public class AuthController : Controller { private readonly IConfiguration _configuration; private readonly IHttpClientFactory _httpClientFactory; public AuthController(IConfiguration configuration, IHttpClientFactory httpClientFactory) { _configuration = configuration; _httpClientFactory = httpClientFactory; } // 显示自定义登录页 [HttpGet] public IActionResult Login(string returnUrl = "/") { ViewData["ReturnUrl"] = returnUrl; return View(); } // 处理登录表单提交 [HttpPost] public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = "/") { if (!ModelState.IsValid) return View(model); var cognitoSettings = _configuration.GetSection("Authentication:Cognito").Get<CognitoSettings>(); var httpClient = _httpClientFactory.CreateClient(); // 向Cognito发送令牌请求 var requestContent = new FormUrlEncodedContent(new Dictionary<string, string> { {"grant_type", "password"}, {"client_id", cognitoSettings.ClientId}, {"client_secret", cognitoSettings.ClientSecret}, {"username", model.Username}, {"password", model.Password}, {"scope", "openid profile email"} }); var response = await httpClient.PostAsync($"{cognitoSettings.Authority}/oauth2/token", requestContent); if (!response.IsSuccessStatusCode) { ModelState.AddModelError(string.Empty, "用户名/密码错误或用户未激活"); return View(model); } var tokenResponse = await response.Content.ReadFromJsonAsync<TokenResponse>(); if (tokenResponse == null) { ModelState.AddModelError(string.Empty, "认证失败,请重试"); return View(model); } // 生成Cookie凭证完成登录 var claims = new List<Claim> { new Claim(ClaimTypes.Name, model.Username), new Claim(JwtRegisteredClaimNames.Sub, model.Username) }; var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme); await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(identity), new AuthenticationProperties { IsPersistent = model.RememberMe, ExpiresUtc = DateTimeOffset.UtcNow.AddSeconds(tokenResponse.ExpiresIn) }); return LocalRedirect(returnUrl); } // 登出逻辑 [HttpGet] public async Task<IActionResult> Logout() { await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); var cognitoSettings = _configuration.GetSection("Authentication:Cognito").Get<CognitoSettings>(); var logoutUrl = $"{cognitoSettings.Authority}/logout?client_id={cognitoSettings.ClientId}&logout_uri={Url.Encode("/")}"; return Redirect(logoutUrl); } } // 辅助模型类 public class CognitoSettings { public string ClientId { get; set; } = string.Empty; public string ClientSecret { get; set; } = string.Empty; public string Authority { get; set; } = string.Empty; public string CallbackPath { get; set; } = string.Empty; public string SignedOutCallbackPath { get; set; } = string.Empty; } public class LoginViewModel { [Required] public string Username { get; set; } = string.Empty; [Required] [DataType(DataType.Password)] public string Password { get; set; } = string.Empty; public bool RememberMe { get; set; } } public class TokenResponse { [JsonPropertyName("access_token")] public string AccessToken { get; set; } = string.Empty; [JsonPropertyName("expires_in")] public int ExpiresIn { get; set; } }
3. 优化登录视图
确保表单正确绑定模型并显示错误信息:
@model LoginViewModel @{ ViewData["Title"] = "登录"; } <h1>自定义登录</h1> <div class="row"> <div class="col-md-4"> <form asp-action="Login" asp-route-returnUrl="@ViewData["ReturnUrl"]"> <div asp-validation-summary="ModelOnly" class="text-danger"></div> <div class="form-group"> <label asp-for="Username" class="control-label"></label> <input asp-for="Username" class="form-control" /> <span asp-validation-for="Username" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="Password" class="control-label"></label> <input asp-for="Password" class="form-control" /> <span asp-validation-for="Password" class="text-danger"></span> </div> <div class="form-group"> <div class="checkbox"> <label> <input asp-for="RememberMe" /> @Html.DisplayNameFor(model => model.RememberMe) </label> </div> </div> <div class="form-group"> <input type="submit" value="登录" class="btn btn-primary" /> </div> </form> </div> </div>
4. 常见错误排查
- 回调地址不匹配:Cognito客户端配置的回调地址必须与
CallbackPath完全一致,包含HTTP/HTTPS协议 - 客户端密钥错误:核对
ClientSecret与Cognito客户端生成的密钥,注意大小写区分 - 用户未激活:Cognito默认要求邮箱/电话验证,需确保用户完成验证流程
- 授权类型未开启:在Cognito客户端设置中启用
ALLOW_PASSWORD_AUTH授权类型
内容的提问来源于stack exchange,提问作者Carlos Barrientos
相关产品推荐
相关产品推荐

