[英]Injected IPrincipal is anonymous but Web API Controller User object is authenticated
[英]User (IPrincipal) not available on ApiController's constructor using Web Api 2.1 and Owin
我正在使用帶有Asp.Net Identity 2的Web Api 2.1。我試圖在我的ApiController的構造函數上獲取經過身份驗證的用戶(我使用AutoFac來注入我的依賴項),但是當調用構造函數時,User顯示為未經過身份驗證。
我試圖獲取用戶,以便我可以為任何數據庫寫操作生成審計信息。
我正在做的一些事情可以幫助診斷:
我只使用app.UseOAuthBearerTokens
作為Asp.Net Identity 2的身份驗證。這意味着我刪除了在使用Asp.Net創建新的Web Api 2.1項目時默認啟用的app.UseCookieAuthentication(new CookieAuthenticationOptions())
身份2。
在WebApiConfig
內部我正在注入我的存儲庫:
builder.RegisterType<ValueRepository>().As<IValueRepository>().InstancePerRequest();
這是我的控制器:
[RoutePrefix("api/values")]
public class ValuesController : ApiController
{
private IValueRepository valueRepository;
public ValuesController(IValueRepository repo)
{
valueRepository = repo;
// I would need the User information here to pass it to my repository
// something like this:
valueRepository.SetUser(User);
}
protected override void Initialize(System.Web.Http.Controllers.HttpControllerContext controllerContext)
{
base.Initialize(controllerContext);
// User is not avaliable here either...
}
}
但是,如果我檢查構造函數上的User對象,這就是我得到的:
身份驗證正在運行,如果我沒有通過我的令牌,它將以Unauthorized
響應。 如果我傳遞令牌並嘗試從任何方法訪問用戶,則會對其進行身份驗證並正確填充。 它只是在調用時沒有顯示在構造函數上。
在我的WebApiConfig
我正在使用:
public static void Register(HttpConfiguration config)
{
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
// ... other unrelated injections using AutoFac
}
我注意到如果我刪除這一行: config.SuppressDefaultHostAuthentication()
用戶將填充在構造函數上。
這是預期的嗎? 如何在構造函數上獲取經過身份驗證的用戶?
編輯:正如Rikard建議我試圖讓用戶使用Initialize方法,但它仍然無法使用,給我的圖像中描述的內容相同。
問題確實在於config.SuppressDefaultHostAuthentication()
。
Brock Allen撰寫的這篇文章很好地解釋了為什么會這樣。 該方法故意將主體設置為null,以便像cookie這樣的默認身份驗證不起作用。 而是, Web API身份驗證篩選器負責身份驗證部分。
如果您沒有cookie身份驗證,則可以選擇刪除此配置。
這里提到的一個簡潔的解決方案是范圍應用程序的Web API部分,以便您可以僅將此配置分離到特定路徑:
public void Configuration(IAppBuilder app)
{
var configuration = WebApiConfiguration.HttpConfiguration;
app.Map("/api", inner =>
{
inner.SuppressDefaultHostAuthentication();
// ...
inner.UseWebApi(configuration);
});
}
不知道這是否仍然相關,但我有完全相同的問題,如上所述。 我設法使用自定義OWIN中間件組件來解決它。
有關我的應用程序結構的一些信
所以對代碼。 這是我的ICurrentContext接口:
public interface ICurrentContext
{
User CurrentUser { get; set; } // User is my User class which holds some user properties
int? CurrentUserId { get; }
}
和實施:
public class DefaultCurrentContext : ICurrentContext
{
public User CurrentUser { get; set; }
public int? CurrentUserId { get { return User != null ? CurrentUser.Id : (int?)null; } }
}
我還創建了一個OWIN中間件組件:
using System.Threading.Tasks;
using Microsoft.AspNet.Identity;
using Microsoft.Owin;
namespace MyWebApp.Web.AppCode.MiddlewareOwin
{
public class WebApiAuthInfoMiddleware : OwinMiddleware
{
public WebApiAuthInfoMiddleware(OwinMiddleware next)
: base(next)
{
}
public override Task Invoke(IOwinContext context)
{
var userId = context.Request.User.Identity.GetUserId<int>();
context.Environment[MyWebApp.Constants.Constant.WebApiCurrentUserId] = userId;
return Next.Invoke(context);
}
}
}
有關此組件的一些信息:MyWebApp.Constants.Constant.WebApiCurrentUserId是一些字符串常量(您可以使用自己的),我曾經用它來避免拼寫錯誤,因為它在多個地方使用。 基本上這個中間件的作用是,它將當前的UserId添加到OWIN環境字典中,然后調用管道中的下一個動作。
然后我創建了Use *擴展語句,將OMC(OWIN中間件組件)包含到OWIN管道中:
using System;
using Owin;
namespace MyWebApp.Web.AppCode.MiddlewareOwin
{
public static class OwinAppBuilderExtensions
{
public static IAppBuilder UseWebApiAuthInfo(this IAppBuilder @this)
{
if (@this == null)
{
throw new ArgumentNullException("app");
}
@this.Use(typeof(WebApiAuthInfoMiddleware));
return @this;
}
}
}
為了使用這個OMC,我在Useup.Auth.cs中的Bearer token的Use *語句后面放了Use *語句:
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions); // This was here before
// Register AuthInfo to retrieve UserId before executing of Api controllers
app.UseWebApiAuthInfo(); // Use newly created OMC
現在,這個原則的實際用法是在AutoFac的Register方法中(在Web應用程序啟動時調用了一些引導代碼;在我的情況下,這是在我的ICurrentContext實現的Startup類(Startup.cs),Configuration方法中),它是:
private static void RegisterCurrentContext(ContainerBuilder builder)
{
// Register current context
builder.Register(c =>
{
// Try to get User's Id first from Identity of HttpContext.Current
var appUserId = HttpContext.Current.User.Identity.GetUserId<int>();
// If appUserId is still zero, try to get it from Owin.Enviroment where WebApiAuthInfo middleware components puts it.
if (appUserId <= 0)
{
object appUserIdObj;
var env = HttpContext.Current.GetOwinContext().Environment;
if (env.TryGetValue(MyWebApp.Constants.Constant.WebApiCurrentUserId, out appUserIdObj))
{
appUserId = (int)appUserIdObj;
}
}
// WORK: Read user from database based on appUserId and create appUser object.
return new DefaultCurrentContext
{
CurrentUser = appUser,
};
}).As<ICurrentContext>().InstancePerLifetimeScope();
}
在我構建AutoFac的容器(因此是ContainerBuilder類型的輸入參數)的地方調用此方法。
這樣,無論用戶如何通過身份驗證(通過MVC Web應用程序或Web API),我都可以單獨實現CurrentContext。 在我的案例中,Web API調用是從某些桌面應用程序進行的,但數據庫和大多數代碼庫對於MVC App和Web API都是相同的。
不知道它是否正確,但它對我有用。 雖然我仍然有點擔心這會如何表現線程,因為我不確切知道如何在API調用中使用HttpContext.Current行為。 我已經讀過某個地方,OWIN字典是按請求使用的,所以我認為這是安全的方法。 而且我也認為這不是那么簡潔的代碼,而是一個討厭UserId的討厭的黑客。 ;)如果使用此approcah有任何問題,我將不勝感激任何評論。 我已經用這兩個星期了,這是我最接近將UserId放在一個地方(當從AutoFac通過lambda解析ICurrentContext時)。
注意:只要有GetUserId的使用,就可以用原始的GetUserId(返回字符串)實現替換它。 我使用GetUserId的原因是因為我在某種程度上重寫了ASP.NET,因為它使用了int而不是TKey的字符串。 我是根據以下文章完成的: http : //www.asp.net/identity/overview/extensibility/change-primary-key-for-users-in-aspnet-identity
在調用構造函數之后調用Initialize方法之前,不會填充控制器的User屬性,這就是為什么Identity尚未填充授權用戶數據的原因。
我意識到刪除config.SuppressDefaultHostAuthentication()允許我更早地在構造函數中獲取Identity。 但是,如果您使用令牌身份驗證,我不建議這樣做。
Thread.CurrentPrincipical在整個管道中都可用,您可以跳過下面的用戶注冊:
valueRepository.SetUser(User);
和訪問
Thread.CurrentPrincipical
而是在存儲庫中,使存儲庫上下文可識別。 此外,您可以添加上下文層。
如果上述解決方案沒有任何工作,請試試這個:
public ActionResult GetFiles()
{
...
string domainID = System.Web.HttpContext.Current.Request.LogonUserIdentity.Name;
...
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.