为 Web Api 2 和 OWIN 令牌身份验证启用 CORS
Enable CORS for Web Api 2 and OWIN token authentication
我有一个 ASP.NET MVC 5 web 项目 (localhost:81),它使用 Knockoutjs 从我的 WebApi 2 项目 (localhost:82) 调用函数,以便在两个项目之间进行通信我启用 CORS。到目前为止一切正常,直到我尝试对 WebApi.
实施 OWIN 令牌身份验证
要在 Web 上使用 /token 端点Api,我还需要在端点上启用 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);
...
}
令牌配置
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());
}
授权提供者
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
}
身份配置
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;
}
编辑:
1.重要说明是直接打开端点 (localhost:82/token) 有效。
2。从 web 项目调用 Api (localhost:82/api/..) 也可以,因此为 WebApi.
启用了 CORS
我知道您的问题已在评论中解决,但我认为了解导致问题的原因以及如何解决整个 class 问题很重要。
查看您的代码,我可以看到您不止一次为令牌端点设置 Access-Control-Allow-Origin
header:
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
在 GrantResourceOwnerCredentials
方法内部:
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
从 CORS specifications 来看,这本身就是一个问题,因为:
If the response includes zero or more than one Access-Control-Allow-Origin header values, return fail and terminate this algorithm.
在您的场景中,框架将此 header 设置两次,并且了解 CORS 必须如何实施,这将导致在某些情况下删除 header(可能 client-related).
以下问题的答案也证实了这一点:Duplicate Access-Control-Allow-Origin: * causing COR error?
出于这个原因,在调用 ConfigureOAuth
之后将调用移动到 app.UseCors
允许您的 CORS header 仅设置一次(因为 owin 管道在 OAuth 中间件处被中断,并且永远不会到达 Token
端点的 Microsoft CORS 中间件)并使您的 Ajax 调用正常工作。
为了更好的全局解决方案,您可以尝试在 OAuth 中间件调用之前再次放置 app.UseCors
,并删除 GrantResourceOwnerCredentials
.
中的第二个 Access-Control-Allow-Origin
插入
按照以下步骤操作,您的 API 将正常工作:
- 从您的 API.
中删除 任何代码,如 config.EnableCors(), [EnableCors(header:"*"....)]
转到 startup.cs 并 在
行下方添加
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
之前
ConfigureAuth(app);
您还需要安装 Microsoft.owin.cors 包才能使用此功能
不使用app.UseCors()
解决问题
我遇到了同样的问题。我使用 Vue.Js 客户端 和 axois 来访问我的 REST-API 和 cross-corps 。在我的 Owin-Api-Server 上,由于 与其他第 3 方组件的版本冲突 ,我 无法添加 Microsoft.Owin.Cors nuget。所以我无法使用app.UseCors()方法,但我通过使用中间件管道解决了它。
private IDisposable _webServer = null;
public void Start(ClientCredentials credentials)
{
...
_webServer = WebApp.Start(BaseAddress, (x) => Configuration(x));
...
}
public void Configuration(IAppBuilder app)
{
...
// added middleware insted of app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
app.Use<MyOwinMiddleware>();
app.UseWebApi(config);
...
}
public class MyOwinMiddleware : OwinMiddleware
{
public MyOwinMiddleware(OwinMiddleware next) :
base(next)
{ }
public override async Task Invoke(IOwinContext context)
{
var request = context.Request;
var response = context.Response;
response.OnSendingHeaders(state =>
{
var resp = (IOwinResponse)state;
// without this headers -> client apps will be blocked to consume data from this api
if (!resp.Headers.ContainsKey("Access-Control-Allow-Origin"))
resp.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
if (!resp.Headers.ContainsKey("Access-Control-Allow-Headers"))
resp.Headers.Add("Access-Control-Allow-Headers", new[] { "*" });
if (!resp.Headers.ContainsKey("Access-Control-Allow-Methods"))
resp.Headers.Add("Access-Control-Allow-Methods", new[] { "*" });
// by default owin is blocking options not from same origin with MethodNotAllowed
if (resp.StatusCode == (int)HttpStatusCode.MethodNotAllowed &&
HttpMethod.Options == new HttpMethod(request.Method))
{
resp.StatusCode = (int)HttpStatusCode.OK;
resp.ReasonPhrase = HttpStatusCode.OK.ToString();
}
}, response);
await Next.Invoke(context);
}
}
所以我创建了自己的中间件并操纵了响应。 GET 调用只需要 Access-Control-Allow headers 而对于 OPTIONS 调用我还需要操作 StatusCode 因为 axois.post() 在发送 [=42= 之前先用 OPTIONS-method 调用].如果 OPTIONS return StatusCode 405,则永远不会发送 POST。
这解决了我的问题。也许这也可以帮助别人。
我有一个 ASP.NET MVC 5 web 项目 (localhost:81),它使用 Knockoutjs 从我的 WebApi 2 项目 (localhost:82) 调用函数,以便在两个项目之间进行通信我启用 CORS。到目前为止一切正常,直到我尝试对 WebApi.
实施 OWIN 令牌身份验证要在 Web 上使用 /token 端点Api,我还需要在端点上启用 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);
...
}
令牌配置
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());
}
授权提供者
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
}
身份配置
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;
}
编辑:
1.重要说明是直接打开端点 (localhost:82/token) 有效。
2。从 web 项目调用 Api (localhost:82/api/..) 也可以,因此为 WebApi.
启用了 CORS我知道您的问题已在评论中解决,但我认为了解导致问题的原因以及如何解决整个 class 问题很重要。
查看您的代码,我可以看到您不止一次为令牌端点设置 Access-Control-Allow-Origin
header:
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
在 GrantResourceOwnerCredentials
方法内部:
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
从 CORS specifications 来看,这本身就是一个问题,因为:
If the response includes zero or more than one Access-Control-Allow-Origin header values, return fail and terminate this algorithm.
在您的场景中,框架将此 header 设置两次,并且了解 CORS 必须如何实施,这将导致在某些情况下删除 header(可能 client-related).
以下问题的答案也证实了这一点:Duplicate Access-Control-Allow-Origin: * causing COR error?
出于这个原因,在调用 ConfigureOAuth
之后将调用移动到 app.UseCors
允许您的 CORS header 仅设置一次(因为 owin 管道在 OAuth 中间件处被中断,并且永远不会到达 Token
端点的 Microsoft CORS 中间件)并使您的 Ajax 调用正常工作。
为了更好的全局解决方案,您可以尝试在 OAuth 中间件调用之前再次放置 app.UseCors
,并删除 GrantResourceOwnerCredentials
.
Access-Control-Allow-Origin
插入
按照以下步骤操作,您的 API 将正常工作:
- 从您的 API. 中删除 任何代码,如
转到 startup.cs 并 在
行下方添加app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
config.EnableCors(), [EnableCors(header:"*"....)]
之前
ConfigureAuth(app);
您还需要安装 Microsoft.owin.cors 包才能使用此功能
不使用app.UseCors()
解决问题我遇到了同样的问题。我使用 Vue.Js 客户端 和 axois 来访问我的 REST-API 和 cross-corps 。在我的 Owin-Api-Server 上,由于 与其他第 3 方组件的版本冲突 ,我 无法添加 Microsoft.Owin.Cors nuget。所以我无法使用app.UseCors()方法,但我通过使用中间件管道解决了它。
private IDisposable _webServer = null;
public void Start(ClientCredentials credentials)
{
...
_webServer = WebApp.Start(BaseAddress, (x) => Configuration(x));
...
}
public void Configuration(IAppBuilder app)
{
...
// added middleware insted of app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
app.Use<MyOwinMiddleware>();
app.UseWebApi(config);
...
}
public class MyOwinMiddleware : OwinMiddleware
{
public MyOwinMiddleware(OwinMiddleware next) :
base(next)
{ }
public override async Task Invoke(IOwinContext context)
{
var request = context.Request;
var response = context.Response;
response.OnSendingHeaders(state =>
{
var resp = (IOwinResponse)state;
// without this headers -> client apps will be blocked to consume data from this api
if (!resp.Headers.ContainsKey("Access-Control-Allow-Origin"))
resp.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
if (!resp.Headers.ContainsKey("Access-Control-Allow-Headers"))
resp.Headers.Add("Access-Control-Allow-Headers", new[] { "*" });
if (!resp.Headers.ContainsKey("Access-Control-Allow-Methods"))
resp.Headers.Add("Access-Control-Allow-Methods", new[] { "*" });
// by default owin is blocking options not from same origin with MethodNotAllowed
if (resp.StatusCode == (int)HttpStatusCode.MethodNotAllowed &&
HttpMethod.Options == new HttpMethod(request.Method))
{
resp.StatusCode = (int)HttpStatusCode.OK;
resp.ReasonPhrase = HttpStatusCode.OK.ToString();
}
}, response);
await Next.Invoke(context);
}
}
所以我创建了自己的中间件并操纵了响应。 GET 调用只需要 Access-Control-Allow headers 而对于 OPTIONS 调用我还需要操作 StatusCode 因为 axois.post() 在发送 [=42= 之前先用 OPTIONS-method 调用].如果 OPTIONS return StatusCode 405,则永远不会发送 POST。
这解决了我的问题。也许这也可以帮助别人。