简体   繁体   中英

How to get Windows Authenticated user in C# Web API sync and async

I need some helping to solve this issue I'm stuck with at work. We have an Web API in .NET Framework 4.7.1 (It has an API, Application, Core, Test projects). The project is complex and it has code from 10+ different developers. We use Microsoft Dynamics D365 8.2 OnPremise for our data, so anytime we have to do CRUD operations we have to connect with the CRM via their SDK code. The project is hosted on IIS 8.5 Windwos Server 2012 R2 and the front-end is Angular 5 (we use the withCredentials option for API calls). We have Windows Authentication and ASP.NET Impersonation turned on and all other Authetication methods disabled. On our C# code, 99% of the times we connect to our CRM SDK we also need to impersonate the user, we do this by passing the username to get a Guid. This code has been working for a long time, until very recently we found out our code was setting the user incorrectly randomly.

The code that sometimes doesn't work is:

System.Security.Principal.WindowsPrincipal(System.Security.Principal.WindowsIdentity.GetCurrent()).Identity.Name

Most of the times we get the correct user but sometimes it gets the user running the process in IIS.

I also tried:

System.Web.HttpContext.Current.User.Identity.Name

But sometimes this is null

And lastly what I thought the solution was:

System.Threading.Thread.CurrentPrincipal.Identity.Name

This worked for many of my tests. The problem is, after a while (I think if the service gets called inside a Task.Run(() => {}); block it gives an error: System.ObjectDisposedException: Safe handle has been closed

I read the following articles to help: official Documentation about .net web api: https://docs.microsoft.com/en-us/aspnet/web-api/overview/security/authentication-and-authorization-in-aspnet-web-api

This post talking about a very similar issue: https://social.msdn.microsoft.com/Forums/vstudio/en-US/bca4556e-53f3-427c-a51a-9b955e2395e2/how-to-get-the-windows-identity-through-web-api-2-in-a-intranetlocal-network-setup?forum=wcf#89485c63-352b-434a-862b-8c5f4cec620c

These two post about the Safe Handle has been closed: Retaining principal inside queued background work item Set Thread.CurrentPrincipal Asynchronously?

which made me try to write some code like (kept the System.Threading.Thread.CurrentPrincipal.Identity.Name working but still throwing the error):

    ClaimsPrincipal principal = new ClaimsPrincipal(Thread.CurrentPrincipal);
    // Save Total Payment Stream Payments (Async)
    Task.Run(() =>
    {
        HttpContext.Current.User = new ClaimsPrincipal(new ClaimsPrincipal(new ClaimsIdentity(new[] { new Claim("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", principal.Identity.Name) }, "auth")));
        Thread.CurrentPrincipal = principal;
        SaveTotalPaymentStream(opportunityTermId);
    });

Any ideas or suggestions about this would be great. Maybe I need something more complex like creating a custom Principal or maybe I'm just messing up the Task.Run (which I finally thought I had it figured out...)

Thanks!

What I learned is that getting the logged on user in async methods can be pretty tricky. The HttpContext is pretty reliable for sync code and when I absolutely need the logged in user I just pass a variable into my async code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM