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

.NET 8 Identity Endpoints Cookie跨应用授权问题求助

跨应用Cookie共享与客户端Cookie获取问题

场景概述

使用.NET Identity平台及Identity API端点构建身份微服务,包含三个测试应用:

  • Identity API:验证用户凭证并生成授权Cookie
  • 客户端Razor Pages应用:向Identity API发送登录请求,需获取Cookie用于后续认证
  • 测试API:模拟微服务,需通过Identity API的Cookie完成认证

现有代码

Identity API 的 Program.cs 代码

using IdentityService.Data;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using System.Security.Claims;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

builder.Services.AddAuthorization();
builder.Services.AddAuthentication().AddCookie(IdentityConstants.ApplicationScheme)
    .AddBearerToken(IdentityConstants.BearerScheme);

builder.Services.AddIdentityCore<User>()
    .AddEntityFrameworkStores<AppDBContext>()
    .AddApiEndpoints();

builder.Services.AddDbContext<AppDBContext>(options =>
{
    options.UseSqlServer(builder.Configuration.GetConnectionString("IdentityDatabase"));
});
var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.MapIdentityApi<User>();

app.Run();

客户端Razor Pages 登录Post方法代码

public async Task<IActionResult> OnPostAsync()
{            
    if (!ModelState.IsValid)
    {
        return Page();
    }
    LoginRequest request = new LoginRequest
    {
        Email = Request.Form["Email"],
        Password = Request.Form["Password"]
    };
    HttpClient client = _httpClientFactory.CreateClient("Auth");
    
    HttpResponseMessage response = await client.PostAsJsonAsync(new Uri(client.BaseAddress + "login?useCookies=true"), request);
    string content = await response.Content.ReadAsStringAsync();
    if (response.IsSuccessStatusCode)
    {
        
        return RedirectToPage("./Test");
    }
    return RedirectToPage("./Index");
}

测试API 的 Program.cs 代码

using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Identity;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddAuthentication(IdentityConstants.ApplicationScheme);
builder.Services.AddAuthorizationBuilder()
    .AddPolicy("api", p =>
    {
        p.RequireAuthenticatedUser();
        p.AddAuthenticationSchemes(IdentityConstants.ApplicationScheme);
    });


builder.Services.AddDataProtection().PersistKeysToFileSystem(new DirectoryInfo(@"c:\temp-keys")).SetApplicationName("IdentityService.API");
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

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

app.MapControllers();

app.MapGet("api/foo", () =>
{
    return new[] { "One", "Two", "Three" };
})
    .RequireAuthorization("api");

app.Run();

测试API 受保护端点代码

namespace IdentityService.TestAPI.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    [Authorize("api")]
    public class TestController : ControllerBase
    {
        private readonly ILogger<TestController> _logger;

        public TestController(ILogger<TestController> logger)
        {
            _logger = logger;
        }

        [HttpGet]
        public string Get()
        {
            return "Test API returned Successfully";
        }
    }
}

问题描述

客户端应用提交登录凭证并收到Identity API的成功响应后,浏览器未获取到预期的Cookie;但通过Swagger直接调用Identity API登录时可正常获取Cookie。需要实现:

  1. 客户端应用的浏览器能获取到Identity API生成的Cookie
  2. 后续浏览器访问测试API时,能自动携带该Cookie完成认证

解决方案

核心问题分析

当前客户端应用是后端代码通过HttpClient调用Identity API,登录成功后Cookie仅存储在后端的HttpClient容器中,并未转发给浏览器,因此浏览器无法获取。同时跨应用Cookie共享需要统一的加密密钥、Cookie配置及CORS设置。

步骤1:修改Identity API配置

1.1 配置Cookie跨域属性

修改AddCookie的配置,确保Cookie支持跨域:

builder.Services.AddAuthentication()
    .AddCookie(IdentityConstants.ApplicationScheme, options =>
    {
        options.Cookie.Name = ".AspNetCore.Identity.Application";
        options.Cookie.Domain = "localhost"; // 本地开发统一域名,生产环境改为实际域名
        options.Cookie.SameSite = SameSiteMode.None; // 跨域场景必须设置为None
        options.Cookie.SecurePolicy = CookieSecurePolicy.Always; // HTTPS环境下强制Secure
        options.Cookie.HttpOnly = true;
    })
    .AddBearerToken(IdentityConstants.BearerScheme);

1.2 配置共享数据保护密钥

确保和其他应用使用相同的密钥容器和应用名称:

builder.Services.AddDataProtection()
    .PersistKeysToFileSystem(new DirectoryInfo(@"c:\temp-keys"))
    .SetApplicationName("IdentityService"); // 三个应用必须使用相同名称

1.3 添加CORS允许客户端凭证

builder.Services.AddCors(options =>
{
    options.AddPolicy("AllowClient", policy =>
    {
        policy.WithOrigins("https://localhost:xxxx") // 替换为客户端应用的实际HTTPS地址
              .AllowAnyHeader()
              .AllowAnyMethod()
              .AllowCredentials(); // 必须允许凭证(Cookie)
    });
});

1.4 启用CORS中间件

在UseHttpsRedirection之后添加:

app.UseCors("AllowClient");

步骤2:修改客户端Razor Pages配置

2.1 转发Identity API的Cookie到浏览器

在登录成功后,将Identity API返回的Set-Cookie头转发给客户端浏览器:

if (response.IsSuccessStatusCode)
{
    // 提取Identity API返回的Cookie并添加到响应中
    foreach (var cookieHeader in response.Headers.GetValues("Set-Cookie"))
    {
        Response.Headers.Append("Set-Cookie", cookieHeader);
    }
    return RedirectToPage("./Test");
}

2.2 配置HttpClient允许凭证(可选)

如果需要客户端后端后续调用测试API时携带Cookie,可在Program.cs中配置HttpClient:

builder.Services.AddHttpClient("Auth", client =>
{
    client.BaseAddress = new Uri("https://localhost:yyyy"); // 替换为Identity API的地址
})
.ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler
{
    UseCookies = true,
    AllowCredentials = true,
    CookieContainer = new CookieContainer()
});

步骤3:修改测试API配置

3.1 统一Cookie认证配置

修改AddAuthentication的配置,和Identity API保持一致:

builder.Services.AddAuthentication(IdentityConstants.ApplicationScheme)
    .AddCookie(IdentityConstants.ApplicationScheme, options =>
    {
        options.Cookie.Name = ".AspNetCore.Identity.Application";
        options.Cookie.Domain = "localhost";
        options.Cookie.SameSite = SameSiteMode.None;
        options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
        options.Cookie.HttpOnly = true;
    });

3.2 统一数据保护配置

修改应用名称和Identity API一致:

builder.Services.AddDataProtection()
    .PersistKeysToFileSystem(new DirectoryInfo(@"c:\temp-keys"))
    .SetApplicationName("IdentityService"); // 和Identity API保持相同

3.3 添加CORS允许客户端凭证

builder.Services.AddCors(options =>
{
    options.AddPolicy("AllowClient", policy =>
    {
        policy.WithOrigins("https://localhost:xxxx") // 客户端应用地址
              .AllowAnyHeader()
              .AllowAnyMethod()
              .AllowCredentials();
    });
});

3.4 启用CORS中间件

在UseHttpsRedirection之后添加:

app.UseCors("AllowClient");

关键注意事项

  1. HTTPS强制:SameSite=None要求Cookie必须是Secure,因此所有应用必须启用HTTPS(本地开发可使用.NET的开发证书)。
  2. 域名统一:本地开发使用localhost作为统一域名,生产环境需使用相同的根域名(如example.com,子域名identity.example.com、client.example.com等)。
  3. 密钥目录权限:确保三个应用都有权限访问c:\temp-keys目录,否则数据保护无法读取/写入密钥。

内容的提问来源于stack exchange,提问作者Jon Sowers

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 08:14:55