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

.NET 8中如何自定义或移除AddIdentityApiEndpoints生成的指定路由?

.NET 8 自定义/移除IdentityApiEndpoints路由的实用方案

针对你遇到的问题,以下几种方案可以有效实现移除不需要的路由、按条件管控访问的需求:

一、通过端点过滤器拦截/禁用不需要的路由

这是最简单的方案,不需要修改路由注册逻辑,直接在映射Identity API时添加过滤器,拦截并拒绝访问指定端点:

var app = builder.Build();

// 映射Identity API并添加过滤逻辑
app.MapIdentityApi<IdentityUser>()
    .AddEndpointFilter(async (context, next) =>
    {
        var identityMeta = context.HttpContext.GetEndpoint()?.Metadata.OfType<IdentityEndpointMetadata>().FirstOrDefault();
        if (identityMeta == null)
            return await next(context);

        // 定义需要禁用的端点名称(对应默认IdentityApi的端点名称)
        var forbiddenEndpoints = new HashSet<string>
        {
            "Register",
            "ForgotPassword",
            "ResetPassword",
            "ConfirmEmail"
        };

        if (forbiddenEndpoints.Contains(identityMeta.EndpointName))
        {
            return Results.NotFound(); // 或返回403 Forbidden明确拒绝
        }

        return await next(context);
    });

默认IdentityApi的端点名称包括:Register、Login、RefreshToken、Logout、ForgotPassword、ResetPassword、ConfirmEmail、ChangePassword等,可以通过identityMeta.EndpointName准确匹配。

二、彻底从路由表中移除指定端点

如果需要完全移除路由(不在swagger或路由列表中显示),可以在应用启动后遍历并修改端点数据源:

var app = builder.Build();

// 先完成Identity API映射
app.MapIdentityApi<IdentityUser>();

// 获取所有需要移除的Identity端点
var endpointsToRemove = app.DescribeEndpoints()
    .Where(e => e.Metadata.OfType<IdentityEndpointMetadata>().Any())
    .Where(e => e.DisplayName is "Register" or "ForgotPassword")
    .Select(e => e.Endpoint)
    .ToList();

// 遍历数据源移除目标端点
foreach (var endpoint in endpointsToRemove)
{
    foreach (var dataSource in app.Services.GetRequiredService<IEnumerable<EndpointDataSource>>())
    {
        if (dataSource is StaticEndpointDataSource staticDs && staticDs.Endpoints.Contains(endpoint))
        {
            // 创建新的端点集合,排除要移除的项
            var filteredEndpoints = staticDs.Endpoints.Where(e => e != endpoint).ToList();
            // 替换原有数据源
            var newDataSource = new StaticEndpointDataSource(filteredEndpoints);
            // 注意:这里需要替换服务中的数据源,实际项目中可以通过自定义EndpointDataSource实现,或使用第三方扩展简化操作
            // 若不需要严格移除路由,建议使用方案一,实现成本更低
        }
    }
}

注意:彻底修改数据源逻辑较繁琐,若只是隐藏路由不让用户访问,方案一足够满足需求。

三、按自定义条件管控路由访问

要根据角色、IP、时间等条件管控端点访问,同样可以在端点过滤器中实现逻辑:

app.MapIdentityApi<IdentityUser>()
    .AddEndpointFilter(async (context, next) =>
    {
        var identityMeta = context.HttpContext.GetEndpoint()?.Metadata.OfType<IdentityEndpointMetadata>().FirstOrDefault();
        if (identityMeta == null)
            return await next(context);

        // 示例1:仅允许管理员访问修改密码端点
        if (identityMeta.EndpointName == "ChangePassword")
        {
            if (!context.HttpContext.User.IsInRole("Admin"))
            {
                return Results.Forbid();
            }
        }

        // 示例2:仅允许特定IP段访问登录端点
        if (identityMeta.EndpointName == "Login")
        {
            var clientIp = context.HttpContext.Connection.RemoteIpAddress;
            var allowedSubnet = IPAddress.Parse("192.168.1.0");
            var subnetMask = IPAddress.Parse("255.255.255.0");
            
            if (!IsIpInSubnet(clientIp, allowedSubnet, subnetMask))
            {
                return Results.StatusCode(StatusCodes.Status403Forbidden);
            }
        }

        return await next(context);
    });

// 辅助方法:判断IP是否在指定子网内
bool IsIpInSubnet(IPAddress ip, IPAddress subnet, IPAddress mask)
{
    var ipBytes = ip.GetAddressBytes();
    var subnetBytes = subnet.GetAddressBytes();
    var maskBytes = mask.GetAddressBytes();

    for (int i = 0; i < ipBytes.Length; i++)
    {
        if ((ipBytes[i] & maskBytes[i]) != subnetBytes[i])
        {
            return false;
        }
    }
    return true;
}

四、完全自定义Identity端点(高度可控)

如果默认端点逻辑完全不符合需求,可以放弃AddIdentityApiEndpoints,自行编写Auth控制器,完全掌控路由和业务逻辑:

[ApiController]
[Route("api/auth")]
public class AuthController : ControllerBase
{
    private readonly UserManager<IdentityUser> _userManager;
    private readonly SignInManager<IdentityUser> _signInManager;

    public AuthController(UserManager<IdentityUser> userManager, SignInManager<IdentityUser> signInManager)
    {
        _userManager = userManager;
        _signInManager = signInManager;
    }

    [HttpPost("login")]
    public async Task<IActionResult> Login([FromBody] LoginDto dto)
    {
        var user = await _userManager.FindByEmailAsync(dto.Email);
        if (user == null || !await _userManager.CheckPasswordAsync(user, dto.Password))
        {
            return Unauthorized("无效的邮箱或密码");
        }

        // 生成JWT令牌(可使用Identity的TokenProvider或自定义JWT逻辑)
        var authToken = await _userManager.GenerateUserTokenAsync(user, TokenOptions.DefaultProvider, "Login");
        return Ok(new { Token = authToken });
    }

    // 仅实现需要的端点,比如刷新令牌、修改密码等
}

public class LoginDto
{
    [Required]
    public string Email { get; set; }
    [Required]
    public string Password { get; set; }
}

这种方式完全避免了默认路由的干扰,适合需要深度定制认证逻辑的场景。

内容的提问来源于stack exchange,提问作者Sir Oliver

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 23:45:18