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