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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 17:42:08