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

ASP.Net Core 6服务访问WindowsIdentity.Name时触发Safe handle已关闭异常

ASP.NET Core 6 Windows身份验证中访问Identity.Name时抛出ObjectDisposedException

问题背景

使用ASP.NET Core 6开发内部Web服务,通过IIS启用Windows身份验证,依赖AuthenticationStateProvider.GetAuthenticationStateAsync().User获取用户身份。多数场景运行正常,但日志中频繁出现访问Windows Identity的Name属性时抛出ObjectDisposedException(提示Safe handle已关闭),未手动释放相关资源,无法定位根源。尝试使用ServerAuthenticationStateProvider但在Blazor Server环境下不可用。

异常详情

System.ObjectDisposedException: Safe handle has been closed.
Object name: 'SafeHandle'.
   at System.StubHelpers.StubHelpers.SafeHandleAddRef(SafeHandle pHandle, Boolean& success)
   at Interop.Advapi32.GetTokenInformation(SafeAccessTokenHandle TokenHandle, UInt32 TokenInformationClass, SafeLocalAllocHandle TokenInformation, UInt32 TokenInformationLength, UInt32& ReturnLength)
   at System.Security.Principal.WindowsIdentity.GetTokenInformation(SafeAccessTokenHandle tokenHandle, TokenInformationClass tokenInformationClass, Boolean nullOnInvalidParam)
   at System.Security.Principal.WindowsIdentity.get_User()
   at System.Security.Principal.WindowsIdentity.<GetName>b__55_0()
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location ---
   at System.Security.Principal.WindowsIdentity.RunImpersonatedInternal(SafeAccessTokenHandle token, Action action)
   at System.Security.Principal.WindowsIdentity.GetName()
   at Server.Common.Services.ClaimsPrincipalService.GetCurrentUserAccountName() 

相关代码

ClaimsPrincipalService实现

public class ClaimsPrincipalService
{
    private readonly AuthenticationStateProvider AuthenticationStateProvider;
    private readonly Serilog.ILogger Logger;

    public ClaimsPrincipalService(AuthenticationStateProvider authenticationStateProvider, Serilog.ILogger logger)
    {
        AuthenticationStateProvider = authenticationStateProvider;
        Logger = logger;
    }

    public async Task<string?> GetCurrentUserAccountName()
    {
        try
        {
            var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
            var user = authState.User;

            if (user.Identity != null && user.Identity.IsAuthenticated && user.Identity.Name != null)
            {
                var identityName = user.Identity.Name;
                int stop = identityName.IndexOf(@"\");
                var userId = stop > -1 ? identityName.Substring(stop + 1, identityName.Length - stop - 1) : string.Empty;
                userId = userId.ToUpper();
                return userId;
            }
            else
            {
                Logger.Warning("User Identity/Authentication could not be verified");
            }
        }
        catch (Exception ex)
        {
            Logger.Error(ex, "Exception during GetClaimsPrincipalDataAsync method");
        }
        return null;
    }
}

服务注册(Program.cs)

builder.Services.AddScoped<ClaimsPrincipalService, ClaimsPrincipalService>();

解决方案分析

1. 立即提取并缓存用户信息

Windows Identity的Name属性内部依赖未托管的安全句柄,请求上下文回收后该句柄会被释放。禁止延迟访问Identity.Name,需在获取AuthenticationState后同步提取所需信息:

public async Task<string?> GetCurrentUserAccountName()
{
    try
    {
        var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
        var user = authState.User;

        if (user.Identity is WindowsIdentity windowsIdentity && windowsIdentity.IsAuthenticated)
        {
            // 同步获取Name并处理,避免后续访问已释放的句柄
            var identityName = windowsIdentity.Name;
            if (!string.IsNullOrEmpty(identityName))
            {
                int stop = identityName.IndexOf(@"\");
                var userId = stop > -1 ? identityName.Substring(stop + 1).ToUpper() : string.Empty;
                return userId;
            }
        }
        Logger.Warning("User Identity/Authentication could not be verified");
    }
    catch (ObjectDisposedException ex)
    {
        Logger.Error(ex, "Windows Identity handle has been disposed");
        // 可根据业务需求添加重试或默认值逻辑
    }
    catch (Exception ex)
    {
        Logger.Error(ex, "Exception during GetCurrentUserAccountName method");
    }
    return null;
}

2. 检查服务生命周期匹配

ClaimsPrincipalService是Scoped服务,绝对不能注入到Singleton服务中,否则会导致请求上下文回收后,Singleton服务仍持有已释放的Identity引用,引发异常。

3. 确认Windows身份验证配置

确保Program.cs中正确配置身份验证:

builder.Services.AddAuthentication(IISDefaults.AuthenticationScheme);
builder.Services.AddAuthorization();

同时在IIS站点配置中启用Windows身份验证,禁用匿名身份验证。

4. Blazor Server环境特殊处理

Blazor Server中AuthenticationState与电路关联,电路断开时Identity句柄会被释放:

  • 在组件/页面初始化阶段立即提取用户信息并缓存
  • 使用CircuitHandler监听电路状态,在电路断开时清理相关缓存

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 08:44:52