.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
相关产品推荐
相关产品推荐

