使用 WebApi 2 临时更改标识

Temporarily Changing Identity with WebApi 2

我有一个 WebApi 控制器,最初验证为特定的 WebApi 用户。对网络的后续访问 api 将通过一个用户执行操作,而无需实际验证为该用户。

我有一些 services/managers 作为 MVC 项目的一部分以适当用户的身份执行功能。我现在想在 WebApi 项目中使用这些服务和管理器,但我不想让用户四处走动。

我希望我可以在用户通过 Web Api 调用验证后临时更改 Web Api 调用的身份,但我想确保当调用已完成,返回的 cookie 用于验证 WebApi 用户,而不是表示为调用一部分的最终用户。

我的问题是,如何才能临时将身份更改为调用中的已验证用户,然后再更改回网络 api 身份?

松散地使用 post 中链接中的代码,我创建了一个 IDisposable 对象,它会临时更改身份。

用法是:

try
{
    using(new Impersonate(userManager, userName))
    {
       /* do your stuff as userName */
    }
}
catch (ImpersonateException) {}

模拟class如下:

public class Impersonate : IDisposable
{
    private UserManager<ApplicationUser> userManager;

    public Impersonate(UserManager<ApplicationUser> userManager, string userName) 
    {
        this.userManager = userManager;

        if (ValidateUser(userName))
        {
            this.ImpersonateUser(userName);
        }
        else
        {
            throw new ImpersonateException("Current user does not have permissions to impersonate user");
        }
    }

    private bool ValidateUser(string userName) 
    {
        /* validate that the current user can impersonate userName */
    }

    public void Dispose()
    {
       this.RevertImpersonation();
    }

    private void ImpersonateUser(string userName) 
    {
       var context = HttpContext.Current;
       var originalUsername = context.User.Identity.Name;

       var impersonatedUser = this.userManager.FindByName(userName);

       var impersonatedIdentity = impersonatedUser.GenerateUserIdentity(this.userManager, "Impersonation");
       impersonatedIdentity.AddClaim(new Claim("UserImpersonation", "true"));
       impersonatedIdentity.AddClaim(new Claim("OriginalUsername", originalUsername));

       var impersonatedPrincipal = new ClaimsPrincipal(impersonatedIdentity);

       context.User = impersonatedPrincipal;
        Thread.CurrentPrincipal = impersonatedPrincipal;
    }

    private void RevertImpersonation()
    {
        var context = HttpContext.Current;

        if (!ClaimsPrincipal.Current.IsImpersonating())
        {
            throw new ImpersonationException("Unable to remove impersonation because there is no impersonation");
        }

        var originalUsername = ClaimsPrincipal.Current.GetOriginalUsername();

        var originalUser = this.userManager.FindByName(originalUsername);

        var originalIdentity = originalUser.GenerateUserIdentity(this.userManager);
        var originalPrincipal = new ClaimsPrincipal(originalIdentity);

        context.User = originalPrincipal;
        Thread.CurrentPrincipal = originalPrincipal;
    }
}

这与链接代码的不同之处在于它仅临时设置标识,因此不需要 SignIn/SignOut。

此外,由于大部分工作是在构造函数中完成的,我不得不删除链接代码使用的异步方面。可能有办法解决它,但我对异步没有足够的经验,也没有足够的耐心来打扰。