Owin Bearer令牌认证+授权控制器

我正在尝试使用Bearer令牌和owin进行身份validation。

我可以使用授权类型password发布令牌,并覆盖AuthorizationServerProvider.cs中的 GrantResourceOwnerCredentials

但我无法使用Authorize属性访问控制器方法。

这是我的代码:

Startup.cs

 public class Startup { public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; } // normal public Startup() : this(false) { } // testing public Startup(bool isDev) { // add settings Settings.Configure(isDev); OAuthOptions = new OAuthAuthorizationServerOptions { AllowInsecureHttp = true, TokenEndpointPath = new PathString("/Token"), AccessTokenExpireTimeSpan = TimeSpan.FromDays(1), Provider = new AuthorizationServerProvider() }; } public void Configuration(IAppBuilder app) { // Configure the db context, user manager and role manager to use a single instance per request app.CreatePerOwinContext(ApplicationDbContext.Create); app.CreatePerOwinContext(ApplicationUserManager.Create); app.CreatePerOwinContext(ApplicationRoleManager.Create); app.CreatePerOwinContext(ApplicationSignInManager.Create); app.CreatePerOwinContext(BaseManager.Create); var config = new HttpConfiguration(); WebApiConfig.Register(config); app.UseWebApi(config); // token generation app.UseOAuthAuthorizationServer(OAuthOptions); app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions { AuthenticationType = "Bearer", AuthenticationMode = AuthenticationMode.Active }); } } 

AuthorizationServerProvider.cs

 public class AuthorizationServerProvider : OAuthAuthorizationServerProvider { public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context) { context.Validated(); } public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context) { context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" }); var userManager = context.OwinContext.GetUserManager(); IdentityUser user = await userManager.FindAsync(context.UserName, context.Password); if (user == null) { context.SetError("invalid_grant", "The user name or password is incorrect."); return; } var identity = new ClaimsIdentity(context.Options.AuthenticationType); identity.AddClaim(new Claim("sub", context.UserName)); identity.AddClaim(new Claim("role", "user")); context.Validated(identity); } } 

WebApiConfig.cs

 public static class WebApiConfig { public static void Register(HttpConfiguration config) { config.MapHttpAttributeRoutes(); // enable CORS for all hosts, headers and methods var cors = new EnableCorsAttribute("*", "*", "*"); config.EnableCors(cors); config.Routes.MapHttpRoute( name: "optional params", routeTemplate: "api/{controller}" ); config.Routes.MapHttpRoute( name: "Default", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); // stop cookie auth config.SuppressDefaultHostAuthentication(); // add token bearer auth config.Filters.Add(new MyAuthenticationFilter()); //config.Filters.Add(new HostAuthenticationFilter(Startup.OAuthOptions.AuthenticationType)); config.Filters.Add(new ValidateModelAttribute()); if (Settings.IsDev == false) { config.Filters.Add(new AuthorizeAttribute()); } // make properties on model camelCased var jsonFormatter = config.Formatters.OfType().First(); jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html")); } 

MyAuthenticationFilter.cs用于调试目的的自定义filter

 public class MyAuthenticationFilter : ActionFilterAttribute, IAuthenticationFilter { public Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken) { if (context.Principal != null && context.Principal.Identity.IsAuthenticated) { } return Task.FromResult(0); } public Task ChallengeAsync(HttpAuthenticationChallengeContext context, CancellationToken cancellationToken) { throw new System.NotImplementedException(); } } 

如果我在MyAuthenticationFilter.cs中调试AuthenticateAsync ,我会在请求中看到标题:

 Authorization: Bearer AQAAANCMnd8BFdERjHoAwE_Cl... 

但身份声明是空的和context.Principal.Identity.IsAuthenticated是假的。

有任何想法吗?

我正在寻找相同的解决方案,我花了一个星期左右的时间,我离开了它。 今天我开始再次搜索,我找到了你的问题,我希望能找到答案。

所以我花了一整天的时间除了尝试所有可能的解决方案,将建议相互融合之外,我找到了一些解决方案,但是我们找到了很长的解决方法,这里的长篇故事就是我发现的。

首先,如果您需要使用自定义第三方身份提供程序令牌对Web站点进行身份validation,则需要使用相同的machineKey来使用它们,或者您需要将它们都放在同一台服务器上。

您需要将machineKey添加到system.web部分,如下所示:

Web.Config中

       

这是一个生成新machineKey的链接:

现在您需要移动到Startup.Auth.cs文件,您可以在其中找到Startup.cs分部类,您需要定义OAuthBearerOptions

Startup.Auth.cs

 public partial class Startup { public static OAuthBearerAuthenticationOptions OAuthBearerOptions { get; private set; } ... public void ConfigureAuth(IAppBuilder app) { // Configure the db context, user manager and signin manager to use a single instance per request app.CreatePerOwinContext(ApplicationDbContext.Create); app.CreatePerOwinContext(ApplicationUserManager.Create); OAuthBearerOptions = new OAuthBearerAuthenticationOptions(); app.UseOAuthBearerAuthentication(OAuthBearerOptions); ... } } 

使用以下内容替换AccountController中的Login操作:

AccountController.cs

 [HttpPost] [AllowAnonymous] [ValidateAntiForgeryToken] public async Task Login(LoginViewModel model, string returnUrl) { /*This will depend totally on how you will get access to the identity provider and get your token, this is just a sample of how it would be done*/ /*Get Access Token Start*/ HttpClient httpClient = new HttpClient(); httpClient.BaseAddress = new Uri("https://youridentityproviderbaseurl"); var postData = new List>(); postData.Add(new KeyValuePair("UserName", model.Email)); postData.Add(new KeyValuePair("Password", model.Password)); HttpContent content = new FormUrlEncodedContent(postData); HttpResponseMessage response = await httpClient.PostAsync("yourloginapi", content); response.EnsureSuccessStatusCode(); string AccessToken = Newtonsoft.Json.JsonConvert.DeserializeObject(await response.Content.ReadAsStringAsync()); /*Get Access Token End*/ If(!string.IsNullOrEmpty(AccessToken)) { var ticket = Startup.OAuthBearerOptions.AccessTokenFormat.Unprotect(AccessToken); var id = new ClaimsIdentity(ticket.Identity.Claims, DefaultAuthenticationTypes.ApplicationCookie); AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = true }, id); return RedirectToLocal(returnUrl); } ModelState.AddModelError("Error", "Invalid Authentication"); return View(); } 

您需要做的最后一件事是将这行代码放在Global.asax.cs中以避免Anti Forgeryexception:

的Global.asax.cs

 public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.NameIdentifier; … } } 

希望这对你有用。

发布一年后,我也遇到了同样的问题。

在此处输入图像描述

如您所见,我的承载令牌在请求标头中被识别,但我的身份仍然未经过身份validation。

要解决此问题,简短的答案是确保在配置WebApi中间件(HttpConfiguration) 之前配置OAuth中间件。

好吧,我一直在研究这个问题已有一段时间了,我终于找出了什么是错的,现在它正在发挥作用。

您的Cors在GrantResourceOwnerCredentials方法上启用代码似乎以某种方式从参数中推翻了标头。 因此,通过将您的第一行放在当前第三行之下,您将解决问题:

  var userManager = context.OwinContext.GetUserManager(); IdentityUser user = await userManager.FindAsync(context.UserName, context.Password); context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" }); 

到目前为止,我还没有深入了解为什么会这样,但我相信通过在获取userManager之前添加新的标头条目以某种方式破坏客户端上的post方法发送的数据,在我的情况下,角度资源如下:

  function userAccount($resource, appSettings) { return { registration: $resource(appSettings.serverPath + "/api/Account/Register", null, { 'registerUser' : { method : 'POST'} } ), login : $resource(appSettings.serverPath + "/Token", null, { 'loginUser': { method: 'POST', headers: { 'Content-Type' : 'application/x-www-form-urlencoded' }, transformRequest: function (data, headersGetter) { var str = []; for (var d in data) { str.push(encodeURIComponent(d) + "=" + encodeURIComponent(data[d])); } return str.join("&"); } } } ) } } 

我不确定这是否有帮助,但我遇到IsAuthenticated在使用dependency injection时返回false的问题(参见此处的 SO问题)并且它看起来因为在注入点它没有被Owin管道设置。

我通过懒惰注入校长来克服它。 无论哪种方式,我把一个非常基本的应用程序(在上面链接)放在一起来演示问题,但它可能会帮助你,因为它显示在属性中设置了Principal并使用了承载认证。