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

Blazor Server .NET Core 6实现点击触发用户证书登录的问题咨询

解决方案:Blazor Server 点击按钮触发客户端证书认证

针对你的需求(支持账号密码+证书登录,点击按钮后才弹出证书选择框),可以通过分离证书认证端点的方式实现,核心思路是让主站点不强制客户端证书,仅对专门的证书登录API路径强制要求证书,点击按钮时发起该API请求触发浏览器的证书选择流程。

步骤1:配置服务器端证书策略

Kestrel 配置(Program.cs)

在Program.cs中,设置主站点允许但不强制客户端证书,同时对/api/certificate-login路径单独强制要求证书:

using Microsoft.AspNetCore.Authentication.Cookies;
using System.Security.Claims;

var builder = WebApplication.CreateBuilder(args);

// 添加Blazor服务
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();

// 配置Cookie认证(用于维持登录会话)
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie(options =>
    {
        options.LoginPath = "/Login";
        options.Cookie.HttpOnly = true;
        options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
        options.Cookie.SameSite = SameSiteMode.Lax;
    });

builder.Services.AddAuthorization();
builder.Services.AddHttpClient();

// 配置Kestrel的HTTPS和客户端证书策略
builder.WebHost.ConfigureKestrel(options =>
{
    options.ConfigureHttpsDefaults(httpsOptions =>
    {
        // 主站点仅接受客户端证书,但不强制
        httpsOptions.ClientCertificateMode = ClientCertificateMode.AllowCertificate;
        // 根据需求开启/关闭吊销检查
        httpsOptions.CheckCertificateRevocation = false;
    });

    // 对证书登录API路径强制要求客户端证书
    options.Map("/api/certificate-login", app =>
    {
        app.UseHttpsRedirection();
        app.UseAuthentication();
        app.UseAuthorization();

        app.Run(async context =>
        {
            // 获取客户端证书
            var clientCert = context.Connection.ClientCertificate;

            // 基础验证:证书是否存在、是否有效
            if (clientCert == null || !clientCert.Verify())
            {
                context.Response.StatusCode = StatusCodes.Status403Forbidden;
                await context.Response.WriteAsync("无效或未提供客户端证书");
                return;
            }

            // 自定义证书验证逻辑(替换为你的业务规则)
            // 示例:检查证书是否由我方CA颁发、主题信息是否匹配用户等
            bool isCertValid = ValidateCustomCertificate(clientCert);
            if (!isCertValid)
            {
                context.Response.StatusCode = StatusCodes.Status403Forbidden;
                await context.Response.WriteAsync("证书不符合系统要求");
                return;
            }

            // 生成用户身份票据,完成登录
            var claims = new List<Claim>
            {
                new Claim(ClaimTypes.Name, clientCert.SubjectName.Name),
                new Claim(ClaimTypes.NameIdentifier, clientCert.Thumbprint),
                // 添加自定义业务声明
            };
            var identity = new ClaimsIdentity(claims, "ClientCertificate");
            var principal = new ClaimsPrincipal(identity);

            await context.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal);

            context.Response.StatusCode = StatusCodes.Status200OK;
            await context.Response.WriteAsync("证书登录成功");
        });
    });
});

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();

app.UseAuthentication();
app.UseAuthorization();

app.MapBlazorHub();
app.MapFallbackToPage("/_Host");

app.Run();

// 自定义证书验证方法
bool ValidateCustomCertificate(System.Security.Cryptography.X509Certificates.X509Certificate2 cert)
{
    // 示例逻辑:检查证书颁发者是否为我方CA
    return cert.IssuerName.Name.Equals("CN=自定义CA名称", StringComparison.OrdinalIgnoreCase);
}

IIS 托管配置

如果使用IIS部署:

  1. 站点级别HTTPS绑定:客户端证书选择接受(而非必需);
  2. 针对/api/certificate-login路径设置强制证书:
    • 右键站点 → 添加应用程序,路径设置为/api/certificate-login,指向同一物理路径;
    • 在该应用程序的SSL设置中,勾选要求客户端证书。

步骤2:Blazor登录页面添加证书登录按钮

在登录页面(如Login.razor)添加按钮,点击时调用证书登录API:

@page "/Login"
@inject HttpClient Http
@inject NavigationManager NavManager

<h3>用户登录</h3>

<!-- 账号密码登录表单 -->
<div class="mb-3">
    <label>用户名</label>
    <input type="text" class="form-control" @bind="Username" />
</div>
<div class="mb-3">
    <label>密码</label>
    <input type="password" class="form-control" @bind="Password" />
</div>
<button class="btn btn-secondary me-2" @onclick="HandlePasswordLogin">账号密码登录</button>

<!-- 证书登录按钮 -->
<button class="btn btn-primary" @onclick="HandleCertificateLogin">证书登录</button>

@code {
    private string Username { get; set; } = string.Empty;
    private string Password { get; set; } = string.Empty;

    private async Task HandlePasswordLogin()
    {
        // 账号密码登录逻辑(略)
    }

    private async Task HandleCertificateLogin()
    {
        try
        {
            var response = await Http.GetAsync("/api/certificate-login");
            if (response.IsSuccessStatusCode)
            {
                // 登录成功,跳转到主页
                NavManager.NavigateTo("/");
            }
            else
            {
                var errorMsg = await response.Content.ReadAsStringAsync();
                // 显示错误提示,可替换为Blazor弹窗
                Console.WriteLine($"证书登录失败:{errorMsg}");
            }
        }
        catch (HttpRequestException)
        {
            // 用户取消证书选择时会触发此异常
            Console.WriteLine("用户取消了证书选择");
        }
    }
}

关键注意事项

  • HTTPS环境要求:浏览器仅在HTTPS连接下才会弹出客户端证书选择框,必须确保站点部署在HTTPS环境;
  • 证书验证安全性:务必实现严格的自定义证书验证逻辑,不能仅依赖cert.Verify(),需检查证书颁发者、有效期、主题信息等;
  • 会话维持:通过Cookie认证维持登录状态,Blazor的SignalR长连接会自动携带Cookie,无需额外配置;
  • 浏览器兼容性:主流浏览器(Chrome、Edge、Firefox)均支持此流程,需注意部分浏览器可能需要用户手动信任CA证书。

内容的提问来源于stack exchange,提问作者Antonio Mora Pérez

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 01:27:11