从 Angular2 从 Web API2 获取令牌导致 CORS 问题或 null

Getting token from web API2 from Angular2 leads to CORS issues or null

我正在尝试制作 sign-in(Angular2 - 客户端和网络 API 2 - 在服务器上)。

我收到了

OPTIONS http://localhost:48604/Token 400 (Bad Request)

其次是

Failed to load http://localhost:48604/Token: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:4200' is therefore not allowed access. The response had HTTP status code 400.

而其他请求不会因 CORS 问题而失败,因为我在 WebApiConfig.cs:

中全局启用了 CORS

网页API:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // Web API configuration and services
        // Configure Web API to use only bearer token authentication.
        config.SuppressDefaultHostAuthentication();
        config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
        EnableCorsAttribute cors = new EnableCorsAttribute("http://localhost:4200", "*", "*");
        config.EnableCors(cors);
        // Web API routes
        config.MapHttpAttributeRoutes();

        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );


    }
}

我的Startup.Auth.cs:

public partial class Startup
{
    public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }

    public static string PublicClientId { get; private set; }

    // For more information on configuring authentication, please visit https://go.microsoft.com/fwlink/?LinkId=301864
    public void ConfigureAuth(IAppBuilder app)
    {
        app.CreatePerOwinContext(ApplicationDbContext.Create);
        app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
        app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);

        app.UseCookieAuthentication(new CookieAuthenticationOptions());
        app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);

        // Configure the application for OAuth based flow
        PublicClientId = "self";
        OAuthOptions = new OAuthAuthorizationServerOptions
        {
            TokenEndpointPath = new PathString("/Token"),
            Provider = new ApplicationOAuthProvider(PublicClientId),
            AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
            AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
            AllowInsecureHttp = true
        };

        // Enable the application to use bearer tokens to authenticate users
        app.UseOAuthBearerTokens(OAuthOptions);

        // ... Code for third-part logins omitted for brevity ...
    }
}

我的 angular 服务:

export class AuthService {

rootUrl = 'http://localhost:48604';

httpOptions = {
    headers: new HttpHeaders({
        'Content-Type': 'application/x-www-form-urlencoded'
    })
};

constructor(private httpClient: HttpClient) {
}

logIn(loginData): Observable<any> {

    const data = 'grant_type=password&username=' + loginData.username + '&password=' + loginData.password;
    return this.httpClient.post(this.rootUrl + '/Token', data, this.httpOptions);
}
}

据我所知,在 /Token 之后,请求应该被重定向到 api/Account/ExternalLogin 但永远不会到达这个控制器方法。

然后我发现 post 他们说你需要覆盖 MatchEndpoint 方法 ApplicationOAuthProvider class我做到了:

        public override Task MatchEndpoint(OAuthMatchEndpointContext context)
    {
        if (context.IsTokenEndpoint && context.Request.Method == "OPTIONS")
        {
            context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "http://localhost:4200" });
            context.OwinContext.Response.Headers.Add("Access-Control-Allow-Headers", new[] { "authorization"});
            context.RequestCompleted();

            return Task.FromResult(0);
        }

        return base.MatchEndpoint(context);
    }

在我的 sign-in 组件中:

    this.authService.logIn(loginData).subscribe(
  (data) => {
    console.log(data);
    //sessionStorage.setItem('tokenKey', token);
    //console.log('sessionStorage.getItem: ' + sessionStorage.getItem('tokenKey'));
  },

);

现在我对 POST 请求的响应是 200 OK 但控制台仍然说:

Failed to load http://localhost:48604/Token: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:4200' is therefore not allowed access.

然后我为 POST 方法添加另一个 if 我还添加了 needed headers:

    public override Task MatchEndpoint(OAuthMatchEndpointContext context)
    {
        if (context.IsTokenEndpoint && context.Request.Method == "OPTIONS")
        {
            context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin",
                new[] {"http://localhost:4200"});
            context.OwinContext.Response.Headers.Add("Access-Control-Allow-Headers", new[] {"authorization"});
            context.RequestCompleted();
            return Task.FromResult(0);
        }

        if (context.IsTokenEndpoint && context.Request.Method == "POST")
        {
            context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "http://localhost:4200" });
            context.OwinContext.Response.Headers.Add("Access-Control-Allow-Headers", new[] { "authorization", "Content-Type" });
            context.RequestCompleted();
            return Task.FromResult(0);
        }
        return base.MatchEndpoint(context);
    }

现在 POST 方法将添加 headers 到响应 但我得到的是 null 而不是令牌。

您可以将 proxy.config.json 文件添加到您的 angular 应用程序以允许 CORS,因为 Angular 在端口 4200 上运行,您的后端可能是其他端口。

https://github.com/angular/angular-cli/blob/master/docs/documentation/stories/proxy.md

然后通过 ng serve --proxy-config proxy.config.json

投放

这会将所有请求重新写入指定端口等(您的后端)

如果您在 1 个包中构建应用程序,那么您不应该有 cors 问题。 ** 构建 angular 然后添加到后端的静态资源 **

好的。问题解决了。如 this video(31:00 分钟)

中所述

所以ASP.NET身份使用OWIN,它需要在Startup.csConfigureAuth方法中启用CORS文件

public partial class Startup
{
    public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }

    public static string PublicClientId { get; private set; }

    // For more information on configuring authentication, please visit https://go.microsoft.com/fwlink/?LinkId=301864
    public void ConfigureAuth(IAppBuilder app)
    {

        app.CreatePerOwinContext(ApplicationDbContext.Create);
        app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
        app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);
        app.UseCors(CorsOptions.AllowAll);
        app.UseCookieAuthentication(new CookieAuthenticationOptions());
        app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);

        // Configure the application for OAuth based flow
        PublicClientId = "self";
        OAuthOptions = new OAuthAuthorizationServerOptions
        {
            TokenEndpointPath = new PathString("/Token"),
            Provider = new ApplicationOAuthProvider(PublicClientId),
            AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
            AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
            AllowInsecureHttp = true
        };

        // Enable the application to use bearer tokens to authenticate users
        app.UseOAuthBearerTokens(OAuthOptions);

        // ... Code for third-part logins omitted for brevity ...
    }
}

现在要做到这一点,我们需要将 nuget 包安装到项目中:

Install-Package Microsoft.Owin.Cors

而且我不再需要在 WebApiConfig 文件中启用 CORS:

EnableCorsAttribute cors = new EnableCorsAttribute("http://localhost:4200", "*", "*");
config.EnableCors(cors);

WebApiConfig 文件

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // Web API configuration and services
        // Configure Web API to use only bearer token authentication.
        config.SuppressDefaultHostAuthentication();
        config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
       // EnableCorsAttribute cors = new EnableCorsAttribute("http://localhost:4200", "*", "*");
        // config.EnableCors(cors);
        // Web API routes
        config.MapHttpAttributeRoutes();

        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );


    }
}

我也不需要重写我之前发布的 MatchEndpoint 方法。

现在,当我发出 sign-in 请求时,我会收到带有令牌的响应:

Request URL: http://localhost:48604/Token

我看到回复 headers 已添加:

Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: http://localhost:4200

当我向另一个 API 控制器发出请求时说:

Request URL: http://localhost:48604/api/UpdateUsrRole

我首先在请求方法中看到:OPTIONS 添加了这些headers:

Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: content-type
Access-Control-Allow-Methods: PUT
Access-Control-Allow-Origin: http://localhost:4200

然后当请求方法:PUT 触发时 添加了这些headers:

Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: http://localhost:4200

就是这样。