wake-up-neo.com

Aufruf von ASP.NET Core 2 API wird umgeleitet (302)

Ich versuche, dieses Projekt https://github.com/asadsahi/AspNetCoreSpa von .net Core 1.1 auf 2.0 zu migrieren, habe aber nach einem erfolgreichen Login ein Problem. Nach dem Login ruft meine GET-API e an. G. zu https: // localhost: 44331/api/profile/test ende mit einer Weiterleitung (302) und ich weiß nicht warum. Ich habe ein Trägerzeichen erhalten und es sieht gut aus.

Headerformat anfordern: Berechtigung: Inhaber [Token]

[Route("api/[controller]")]
public class ProfileController : BaseController
{
    private readonly UserManager<ApplicationUser> _userManager;
    private readonly ILogger _logger;

    public ProfileController(ILoggerFactory loggerFactory, UserManager<ApplicationUser> userManager)
    {
        _logger = loggerFactory.CreateLogger<ProfileController>();
        _userManager = userManager;
    }

    [HttpGet("test")]
    public async Task<IActionResult> Test()
    {
        return  Json(ModelState.GetModelErrors());
    }
}

[Authorize]
[ServiceFilter(typeof(ApiExceptionFilter))]
[ResponseCache(Location = ResponseCacheLocation.None, NoStore = true)]
public class BaseController : Controller
{
    public BaseController()
    {
    }
}

Startup.cs:

public void ConfigureServices(IServiceCollection services)
{
    if (_hostingEnv.IsDevelopment())
    {
        services.AddSslCertificate(_hostingEnv);
    }
    else
    {
        services.Configure<MvcOptions>(o => o.Filters.Add(new RequireHttpsAttribute()));
    }
    services.AddOptions();
    services.AddCors();
    services.AddLogging();
    services.AddResponseCompression(options =>
    {
        options.MimeTypes = Helpers.DefaultMimeTypes;
    });

    services.AddAuthentication(sharedOptions =>
    {
        sharedOptions.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
        sharedOptions.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        sharedOptions.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;


    }).AddJwtBearer(cfg =>
    {
        cfg.SaveToken = true;
        cfg.TokenValidationParameters = new TokenValidationParameters
        {
            ValidIssuer = Configuration["Authentication:BearerTokens:Issuer"],
            ValidAudience = Configuration["Authentication:BearerTokens:Audience"],
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Authentication:BearerTokens:Key"])),
            ValidateIssuerSigningKey = false,
            ValidateLifetime = true,
            ClockSkew = TimeSpan.Zero
        };
        cfg.Events = new JwtBearerEvents
        {

            OnAuthenticationFailed = context =>
            {
                var logger = context.HttpContext.RequestServices.GetRequiredService<ILoggerFactory>().CreateLogger(nameof(JwtBearerEvents));
                logger.LogError("Authentication failed.", context.Exception);
                return Task.CompletedTask;
            },

            OnMessageReceived = context =>
            {
                return Task.CompletedTask;
            },
            OnChallenge = context =>
            {
                var logger = context.HttpContext.RequestServices.GetRequiredService<ILoggerFactory>().CreateLogger(nameof(JwtBearerEvents));
                logger.LogError("OnChallenge error", context.Error, context.ErrorDescription);
                return Task.CompletedTask;
            }
        };
    });

    services.AddDbContext<ApplicationDbContext>(options =>
    {
        string useSqLite = Startup.Configuration["Data:useSqLite"];
        if (useSqLite.ToLower() == "true")
        {
            options.UseSqlite(Startup.Configuration["Data:SqlLiteConnectionString"]);
        }
        else
        {
            options.UseSqlServer(Startup.Configuration["Data:SqlServerConnectionString"]);
        }
        options.UseOpenIddict();
    });


    services.AddIdentity<ApplicationUser, ApplicationRole>()
        .AddEntityFrameworkStores<ApplicationDbContext>()
        .AddDefaultTokenProviders();

    //services.ConfigureApplicationCookie(options =>
    //{

    //    options.LoginPath = "/login";
    //    options.Events.OnRedirectToLogin = context =>
    //    {
    //        if (context.Request.Path.StartsWithSegments("/api") &&
    //            context.Response.StatusCode == (int)HttpStatusCode.OK)
    //        {
    //            context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
    //        }
    //        else
    //        {
    //            context.Response.Redirect(context.RedirectUri);
    //        }
    //        return Task.FromResult(0);
    //    };
    //});


    services.AddOAuthProviders();

    services.AddCustomOpenIddict();

    services.AddMemoryCache();

    services.RegisterCustomServices();

    services.AddAntiforgery(options => options.HeaderName = "X-XSRF-TOKEN");

    services.AddCustomizedMvc();

    // Node services are to execute any arbitrary nodejs code from .net
    services.AddNodeServices();

    services.AddSwaggerGen(c =>
    {
        c.SwaggerDoc("v1", new Info { Title = "AspNetCoreSpa", Version = "v1" });
    });
}

public void Configure(IApplicationBuilder app)
{
    app.AddDevMiddlewares();

    if (_hostingEnv.IsProduction())
    {
        app.UseResponseCompression();
    }

    app.SetupMigrations();

    app.UseXsrf();

    app.UseStaticFiles();

    app.UseAuthentication();

    app.UseMvc(routes =>
    {
        // http://stackoverflow.com/questions/25982095/using-googleoauth2authenticationoptions-got-a-redirect-uri-mismatch-error
        routes.MapRoute(name: "signin-google", template: "signin-google", defaults: new { controller = "Account", action = "ExternalLoginCallback" });

        routes.MapSpaFallbackRoute(
            name: "spa-fallback",
            defaults: new { controller = "Home", action = "Index" });
    });
}

Meine IServiceCollection-Erweiterungen:

public static IServiceCollection AddCustomizedMvc(this IServiceCollection services)
{
    services.AddMvc(options =>
    {
        options.Filters.Add(typeof(ModelValidationFilter));
    })
    .AddJsonOptions(options =>
    {
        options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
    });

    return services;
}

public static IServiceCollection AddOAuthProviders(this IServiceCollection services)
{
    services.AddAuthentication()
        .AddFacebook(o =>
        {
            o.AppId = Startup.Configuration["Authentication:Facebook:AppId"];
            o.AppSecret = Startup.Configuration["Authentication:Facebook:AppSecret"];
        });

    services.AddAuthentication()
        .AddGoogle(o =>
        {
            o.ClientId = Startup.Configuration["Authentication:Google:ClientId"];
            o.ClientSecret = Startup.Configuration["Authentication:Google:ClientSecret"];
        });
    services.AddAuthentication()
        .AddTwitter(o =>
        {
            o.ConsumerKey = Startup.Configuration["Authentication:Twitter:ConsumerKey"];
            o.ConsumerSecret = Startup.Configuration["Authentication:Twitter:ConsumerSecret"];
        });

    services.AddAuthentication()
        .AddMicrosoftAccount(o =>
        {
            o.ClientId= Startup.Configuration["Authentication:Microsoft:ClientId"];
            o.ClientSecret = Startup.Configuration["Authentication:Microsoft:ClientSecret"];
        });

    return services;
}

public static IServiceCollection AddCustomOpenIddict(this IServiceCollection services)
{

    // Configure Identity to use the same JWT claims as OpenIddict instead
    // of the legacy WS-Federation claims it uses by default (ClaimTypes),
    // which saves you from doing the mapping in your authorization controller.
    services.Configure<IdentityOptions>(options =>
    {
        options.ClaimsIdentity.UserNameClaimType = OpenIdConnectConstants.Claims.Name;
        options.ClaimsIdentity.UserIdClaimType = OpenIdConnectConstants.Claims.Subject;
        options.ClaimsIdentity.RoleClaimType = OpenIdConnectConstants.Claims.Role;

    });

    // Register the OpenIddict services.
    services.AddOpenIddict()
        // Register the Entity Framework stores.
        .AddEntityFrameworkCoreStores<ApplicationDbContext>()

        // Register the ASP.NET Core MVC binder used by OpenIddict.
        // Note: if you don't call this method, you won't be able to
        // bind OpenIdConnectRequest or OpenIdConnectResponse parameters.
        .AddMvcBinders()

        // Enable the token endpoint.
        .EnableTokenEndpoint("/connect/token")

        // Enable the password and the refresh token flows.
        .AllowPasswordFlow()
        .AllowRefreshTokenFlow()

        // During development, you can disable the HTTPS requirement.
        .DisableHttpsRequirement()

        // Register a new ephemeral key, that is discarded when the application
        // shuts down. Tokens signed using this key are automatically invalidated.
        // This method should only be used during development.
        .AddEphemeralSigningKey();

    // On production, using a X.509 certificate stored in the machine store is recommended.
    // You can generate a self-signed certificate using Pluralsight's self-cert utility:
    // https://s3.amazonaws.com/pluralsight-free/keith-brown/samples/SelfCert.Zip
    //
    // services.AddOpenIddict()
    //     .AddSigningCertificate("7D2A741FE34CC2C7369237A5F2078988E17A6A75");
    //
    // Alternatively, you can also store the certificate as an embedded .pfx resource
    // directly in this Assembly or in a file published alongside this project:
    //
    // services.AddOpenIddict()
    //     .AddSigningCertificate(
    //          Assembly: typeof(Startup).GetTypeInfo().Assembly,
    //          resource: "AuthorizationServer.Certificate.pfx",
    //          password: "OpenIddict");

    return services;
}

public static IServiceCollection AddCustomDbContext(this IServiceCollection services)
{
    // Add framework services.

    return services;
}

public static IServiceCollection RegisterCustomServices(this IServiceCollection services)
{
    // New instance every time, only configuration class needs so its ok
    services.Configure<SmsSettings>(options => Startup.Configuration.GetSection("SmsSettingsTwillio").Bind(options));
    services.AddTransient<UserResolverService>();
    services.AddTransient<IEmailSender, EmailSender>();
    services.AddTransient<ISmsSender, SmsSender>();
    services.AddScoped<ApiExceptionFilter>();
    return services;
}

Hier meine Pakete: 

<ItemGroup>
<PackageReference Include="AspNet.Security.OAuth.Introspection" Version="2.0.0-*" />
<PackageReference Include="AspNet.Security.OAuth.Validation" Version="2.0.0-*" />
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.Cookies" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.AzureAppServicesIntegration" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Cors" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Antiforgery" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.Google" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.Facebook" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.MicrosoftAccount" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.Twitter" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel.Https" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.ResponseCompression" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.AngularServices" Version="1.1.0-beta-000002" />
<PackageReference Include="AspNet.Security.OAuth.GitHub" Version="1.0.0-beta3-final" />
<PackageReference Include="AspNet.Security.OAuth.LinkedIn" Version="1.0.0-beta3-final" />
<PackageReference Include="OpenIddict" Version="2.0.0-*" />
<PackageReference Include="OpenIddict.EntityFrameworkCore" Version="2.0.0-*" />
<PackageReference Include="OpenIddict.Mvc" Version="2.0.0-*" />
<PackageReference Include="SendGrid" Version="9.9.0" />
<PackageReference Include="MailKit" Version="1.18.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="1.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="1.0.0" />
<PackageReference Include="Twilio" Version="5.6.3" />
<PackageReference Include="Stripe.net" Version="10.4.0" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
<PackageReference Include="Webpack" Version="4.0.0" />
<PackageReference Include="Serilog" Version="2.5.0" />
<PackageReference Include="Serilog.Extensions.Logging" Version="2.0.2" />
<PackageReference Include="Serilog.Sinks.Seq" Version="3.3.3" />
<PackageReference Include="Bogus" Version="17.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.0.0">
  <PrivateAssets>All</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.0.0">
  <PrivateAssets>All</PrivateAssets>
</PackageReference>

  <PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.ViewCompilation" Version="2.0.0" PrivateAssets="All" />
  </ItemGroup>

  <ItemGroup>
<DotNetCliToolReference Include="Microsoft.DotNet.Watcher.Tools" Version="2.0.0" />
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.0" />
<DotNetCliToolReference Include="Microsoft.Extensions.SecretManager.Tools" Version="2.0.0" />
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.0" />
  </ItemGroup>

Hier sind meine Protokolle:

info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
  Request starting HTTP/1.1 GET http://localhost:44331/api/profile/test 
application/json; charset=UTF-8 
info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2]
  Authorization failed for user: (null).
info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2]
  Authorization failed for user: (null).
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[3]
  Authorization failed for the request at filter 
'Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter'.
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[3]
  Authorization failed for the request at filter 
'Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter'.
info: Microsoft.AspNetCore.Mvc.ChallengeResult[1]
  Executing ChallengeResult with authentication schemes ().
info: Microsoft.AspNetCore.Mvc.ChallengeResult[1]
  Executing ChallengeResult with authentication schemes ().
info: 
Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler[12]
  AuthenticationScheme: Identity.Application was challenged.
info: 
Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler[12]
  AuthenticationScheme: Identity.Application was challenged.
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2]
  Executed action 
AspNetCoreSpa.Server.Controllers.api.ProfileController.Test (AspNetCoreSpa) 
in 43.3105ms
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2]
  Executed action 
AspNetCoreSpa.Server.Controllers.api.ProfileController.Test (AspNetCoreSpa) 
in 43.3105ms
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
  Request finished in 67.4133ms 302 
infoinfo: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
  Request finished in 67.4133ms 302 
: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
  Request starting HTTP/1.1 GET http://localhost:44331/Account/Login?
ReturnUrl=%2Fapi%2Fprofile%2Ftest application/json; charset=UTF-8 
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
  Request starting HTTP/1.1 GET http://localhost:44331/Account/Login?
ReturnUrl=%2Fapi%2Fprofile%2Ftest application/json; charset=UTF-8 
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1]
  Executing action method 
AspNetCoreSpa.Server.Controllers.HomeController.Index (AspNetCoreSpa) with 
arguments ((null)) - ModelState is Valid
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1]
  Executing action method 
AspNetCoreSpa.Server.Controllers.HomeController.Index (AspNetCoreSpa) with 
arguments ((null)) - ModelState is Valid
info: Microsoft.AspNetCore.Mvc.ViewFeatures.Internal.ViewResultExecutor[1]
  Executing ViewResult, running view at path /Views/Home/Index.cshtml.
info: Microsoft.AspNetCore.Mvc.ViewFeatures.Internal.ViewResultExecutor[1]
  Executing ViewResult, running view at path /Views/Home/Index.cshtml.
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2]
  Executed action AspNetCoreSpa.Server.Controllers.HomeController.Index 
(AspNetCoreSpa) in 13.2746ms
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2]
  Executed action AspNetCoreSpa.Server.Controllers.HomeController.Index 
(AspNetCoreSpa) in 13.2746ms
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
  Request finished in 79.2352ms 200 text/html; charset=utf-8
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
  Request finished in 79.2352ms 200 text/html; charset=utf-8

Ich wundere mich über die folgende Zeile:

Berechtigung für Benutzer fehlgeschlagen: (null)

Ich habe bereits festgestellt, dass diese Autorisierung für Benutzer fehlgeschlagen ist: (null) , Aber es gibt noch keine Antwort, und ich denke, es handelt sich um ein .NET Core 1-Problem.

8
Simon Arendt

Ich bin auf das gleiche Problem gestoßen und um das Problem zu lösen, musste ich das Authentifizierungsschema in das Authorize-Attribut des Controllers aufnehmen.

In Ihrem Fall:

[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[ServiceFilter(typeof(ApiExceptionFilter))]
[ResponseCache(Location = ResponseCacheLocation.None, NoStore = true)]
public class BaseController : Controller
{
    public BaseController()
    {
    }
}
5
mjegen

Wenn Sie AddIdentity aufrufen, wird die Cookie-Authentifizierung hinzugefügt, wodurch die beabsichtigte JWT-Trägerauthentifizierung überschrieben wird. Um dies zu umgehen, können Sie den AddIdentity-Anruf verschieben, bevor Sie die JWT-Authentifizierung einrichten. Der folgende Code ist für mich geeignet:

// setup identity
services.AddIdentity<ApplicationUser, ApplicationRole>()
    .AddEntityFrameworkStores<MyMoneyDbContext>()
    .AddDefaultTokenProviders();

// setup Jwt authentication
services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, jwtBearerOptions =>
{
    jwtBearerOptions.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuerSigningKey = true,
        ...

Eine andere Alternative ist die Verwendung von AddIdentityCore, aber das habe ich nie versucht.

3
Codism

Für mich war es mit Cross-Origin Resource Sharing (CORS) verbunden. Ich habe eine API in einem App Service in Azure mit aktiviertem CORS. Ich habe die 302 zum Verschwinden gebracht, als ich die Aufrufer der API zur Liste der zulässigen Herkunftsorte hinzugefügt habe.

Ich musste dies tun, obwohl ich diese Ursprünge bereits in meinem Aspnet Core-Startcode hinzugefügt hatte.

0
Kristoffer