为Web Api 2和OWIN令牌身份验证启用CORS

Sam*_*Sam 27 cors asp.net-web-api owin asp.net-mvc-5 asp.net-web-api2

我有一个ASP.NET MVC 5 webproject(localhost:81),它使用Knockoutjs从我的WebApi 2项目(localhost:82)调用函数,以便在我启用CORS的两个项目之间进行通信.到目前为止,一切都有效,直到我尝试对WebApi实施OWIN令牌认证.

要在WebApi上使用/ token端点,我还需要在端点上启用CORS,但经过几个小时的尝试和搜索解决方案后,它仍在运行,并且api/token仍然会导致:

XMLHttpRequest cannot load http://localhost:82/token. No 'Access-Control-Allow-Origin' header is present on the requested resource. 

public void Configuration(IAppBuilder app)
{
    app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
    TokenConfig.ConfigureOAuth(app);
    ...
}
Run Code Online (Sandbox Code Playgroud)

TokenConfig

public static void ConfigureOAuth(IAppBuilder app)
{
    app.CreatePerOwinContext(ApplicationDbContext.Create);
    app.CreatePerOwinContext<AppUserManager>(AppUserManager.Create);

    OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
    {
        AllowInsecureHttp = true,
        TokenEndpointPath = new PathString("/token"),
        AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
        Provider = new SimpleAuthorizationServerProvider()
    };

    app.UseOAuthAuthorizationServer(OAuthServerOptions);
    app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}
Run Code Online (Sandbox Code Playgroud)

AuthorizationProvider

public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
    context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });

    var appUserManager = context.OwinContext.GetUserManager<AppUserManager>();
    IdentityUser user = await appUserManager.FindAsync(context.UserName, context.Password);

    if (user == null)
    {
        context.SetError("invalid_grant", "The user name or password is incorrect.");
        return;
    }
    ... claims
}
Run Code Online (Sandbox Code Playgroud)

IdentityConfig

public static AppUserManager Create(IdentityFactoryOptions<AppUserManager> options, IOwinContext context)
{
    // Tried to enable it again without success. 
    //context.Response.Headers.Add("Access-Control-Allow-Origin", new[] {"*"});

    var manager = new AppUserManager(new UserStore<AppUser>(context.Get<ApplicationDbContect>()));

    ...

    var dataProtectionProvider = options.DataProtectionProvider;
    if (dataProtectionProvider != null)
    {
        manager.UserTokenProvider =
                new DataProtectorTokenProvider<AppUser>(dataProtectionProvider.Create("ASP.NET Identity"));
    }
    return manager;
}
Run Code Online (Sandbox Code Playgroud)

编辑:

1.重要提示是直接打开端点(localhost:82/token).

2.从webproject调用Api(localhost:82/api/..)也可以,因此为WebApi启用了CORS.

Fed*_*uma 42

我知道你的问题在评论中得到了解决,但我认为重要的是要了解导致它的原因以及如何解决这一类问题.

查看代码,我可以看到您Access-Control-Allow-Origin为Token端点多次设置标头:

app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
Run Code Online (Sandbox Code Playgroud)

内部GrantResourceOwnerCredentials方法:

context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" }); 
Run Code Online (Sandbox Code Playgroud)

考虑到CORS规范,这本身就是一个问题,因为:

如果响应包含零个或多个Access-Control-Allow-Origin标头值,则返回失败并终止此算法.

在您的方案中,框架将设置此标头两次,并了解必须如何实现CORS,这将导致在某些情况下删除标头(可能与客户端相关).

以下问题答案也证实了这一点:Duplicate Access-Control-Allow-Origin:*导致COR错误?

因此,在调用app.UseCors之后将调用移动到ConfigureOAuth允许您的CORS头只能设置一次(因为owin管道在OAuth中间件中被中断,并且永远不会到达Token端点的Microsoft CORS中间件)并使您的Ajax调用工作.

为了获得更好的全局解决方案,您可以尝试app.UseCors在OAuth中间件调用之前再次放置,并删除Access-Control-Allow-Origin内部的第二个插入GrantResourceOwnerCredentials.


小智 9

按照以下步骤操作,您的API将正常运行:

  1. config.EnableCors(), [EnableCors(header:"*"....)]从您的API中删除任何代码.
  2. 转到startup.cs并添加以下行

    app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
    
    Run Code Online (Sandbox Code Playgroud)

之前

    ConfigureAuth(app);
Run Code Online (Sandbox Code Playgroud)

您还需要安装Microsoft.owin.cors包才能使用此功能