1

Ich habe eine Web App, die ich mit .NET Core 2 MVC mit individuellen Benutzerkonten erstellt. Die App funktioniert einwandfrei und die Authorize auf den Controllern funktioniert super, zeigt nur erlaubte Ansicht etc. an.Core 2 Autorisieren auf API Controller

Es ist alles Standard, gebaut mit Anleitung aus den verschiedenen Tutorials online. Es macht nichts zu clever, einfache Formen, die CRUD-Operationen ausführen.

Ich möchte einige REST-Endpunkte hinzufügen, die JSON mit dieser Anwendung austauschen und die Endpunkte autorisieren, die eine JWT als Autorisierungskopf (Bearer) verwenden. Laut allen Tutorials sollte das einigermaßen unkompliziert sein, da sie kombiniert wurden, aber ich kann scheinbar nichts zur Arbeit bringen.

Was scheint zu passieren ist, die MVC Authorize überschreibt die JWTBearer Autorisierung, so dass ich nur auf die API-Aktionen zugreifen kann (die ich als/api/{aktion} routen möchte), wenn ich einen eingeloggten Cookie habe.

  1. Ich brauche die MVC Zeug mit Genehmigung in Ruhe gelassen werden. Das funktioniert gut.
  2. Ich möchte API-Endpunkte unter/api/{controller}/{aktion}/{id} hinzufügen. Es macht nichts, wenn dies in der gleichen Steuerung oder einem anderen Controller
  3. Die Autorisierung on/api sollte sein durch ein JWT-Bearer-Token und den MVC-Stuff durch den Login-Cookie als Standard. Beide Karten jedoch auf den gleichen Benutzer (ownerID)

Kann mir jemand in die richtige Richtung zeigen? Ich habe versucht, eine einfache GET-Methode für 3 Tage zu implementieren, und ich treffe immer wieder Ziegelwände.

EDIT: Weitere Tests haben etwas interessantes ergeben. Ich habe Swagger installiert, um die Anfragen zu testen.

Ich habe einen zweiten Controller hinzugefügt, um meine API-Methoden zu behandeln. Dies ist auf api/Rennen. Dieser Controller verfügt über das JWTBearerDefaults als das Authentifizierungsschema.

[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] 
[Route("api/[controller]")] 
  • Wenn ich nicht in durch den MVC-app angemeldet bin, und eine Anfrage ohne Träger Token machen, es leitet mich einzuloggen.
  • Wenn ich nicht über die MVC-App angemeldet bin und die Anfrage MIT einem (gültigen) Token mache, leitet es mich zur Anmeldung weiter.
  • Wenn ich durch MVC anmelden und meine Anfrage ausführen, ohne eine Bearer Token erhalte ich eine 401 Unauthorized (erwartet)
  • Als ich (noch) bin angemeldet und meine Anfrage mit einem gültigen Inhaber Token ausführen ich eine gültige Antwort erhalten.
  • Als ich noch in einer Ausführungs meiner Anfrage mit einem ungültigen Inhaber Token angemeldet bin bekomme ich eine 401 unauthorized (erwartet)

So scheint es, wie es die Token-Authentifizierung als eine zweite Schicht der Zulassung verwendet. Was ich möchte, ist, dass es bei den/api-Controllern als einzige Autorisierungsmethode verwendet wird. Hier

ist der Code von meinem startup.cs

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Threading.Tasks; 
using Microsoft.AspNetCore.Builder; 
using Microsoft.AspNetCore.Identity; 
using Microsoft.EntityFrameworkCore; 
using Microsoft.AspNetCore.Hosting; 
using Microsoft.Extensions.Configuration; 
using Microsoft.Extensions.DependencyInjection; 
using TechsportiseOnline.Data; 
using TechsportiseOnline.Models; 
using TechsportiseOnline.Services; 
using Microsoft.AspNetCore.Mvc; 
using Microsoft.AspNetCore.Authorization; 
using Microsoft.AspNetCore.Mvc.Authorization; 
using TechsportiseOnline.Authorization; 
using TechsportiseOnline.Helpers; 
using Swashbuckle.AspNetCore.Swagger; 
using System.IO; 
using Microsoft.Extensions.PlatformAbstractions; 
using static TechsportiseOnline.Helpers.Swagger; 
using Microsoft.AspNetCore.Authentication.JwtBearer; 
using Microsoft.IdentityModel.Tokens; 
using System.Text; 

namespace TechsportiseOnline 
{ 
    public class Startup 
    { 
     public Startup(IConfiguration configuration) 
     { 
      Configuration = configuration; 
     } 

     public IConfiguration Configuration { get; } 

     // This method gets called by the runtime. Use this method to add services to the container. 
     public void ConfigureServices(IServiceCollection services) 
     { 
      services.AddDbContext<ApplicationDbContext>(options => 
       options.UseSqlServer(Configuration.GetConnectionString("TechsportiseDB"))); 
                 //options.UseInMemoryDatabase("Teschsportise")); 

      services.AddIdentity<ApplicationUser, IdentityRole>(config => 
       { 
        config.SignIn.RequireConfirmedEmail = true; 
       }) 
       .AddEntityFrameworkStores<ApplicationDbContext>() 
       .AddDefaultTokenProviders(); 

      services.Configure<IdentityOptions>(options => 
      { 
       // Password settings 
       options.Password.RequireDigit = true; 
       options.Password.RequiredLength = 6; 
       options.Password.RequireNonAlphanumeric = false; 
       options.Password.RequireUppercase = false; 
       options.Password.RequireLowercase = false; 
       options.Password.RequiredUniqueChars = 2; 

       // Lockout settings 
       options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(30); 
       options.Lockout.MaxFailedAccessAttempts = 10; 
       options.Lockout.AllowedForNewUsers = true; 

       // User settings 
       options.User.RequireUniqueEmail = true; 
      }); 

      services.Configure<AuthMessageSenderOptions>(Configuration); 

      services.ConfigureApplicationCookie(options => 
      { 
       // Cookie settings 
       options.Cookie.HttpOnly = true; 
       options.Cookie.Expiration = TimeSpan.FromDays(150); 
       options.LoginPath = "/Account/Login"; // If the LoginPath is not set here, ASP.NET Core will default to /Account/Login 
       options.LogoutPath = "/Account/Logout"; // If the LogoutPath is not set here, ASP.NET Core will default to /Account/Logout 
       options.AccessDeniedPath = "/Account/AccessDenied"; // If the AccessDeniedPath is not set here, ASP.NET Core will default to /Account/AccessDenied 
       options.SlidingExpiration = true; 
      }); 

      // Add application services. 
      services.AddTransient<IEmailSender, Email>(); 
      //services.AddTransient<ICreateContact>(); 
      //services.AddTransient<IUpdateContact>(); 

      services.AddSwaggerGen(c => 
      { 
       c.SwaggerDoc("v1", new Info { Title = "Techsportise API", Version = "v1" }); 
       c.OperationFilter<AddRequiredHeaderParameter>(); 
       var filePath = Path.Combine(PlatformServices.Default.Application.ApplicationBasePath, "Techsportise.xml"); 
       c.IncludeXmlComments(filePath); 
      }); 

      services.Configure<JWTSettings>(Configuration.GetSection("JWTSettings")); 


      services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) 
      .AddJwtBearer(options => 
      { 
       options.RequireHttpsMetadata = false; 
       options.IncludeErrorDetails = true; 

       var secretKey = Configuration.GetSection("JWTSettings:SecretKey").Value; 
       var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(secretKey)); 

       options.TokenValidationParameters = new TokenValidationParameters 
       { 

        ValidateIssuer = true, 
        ValidIssuer = Configuration.GetSection("JWTSettings:Issuer").Value, 
        ValidateAudience = true, 
        ValidAudience = Configuration.GetSection("JWTSettings:Audience").Value, 
        ValidateIssuerSigningKey = true, 
        IssuerSigningKey = signingKey, 

       }; 
      }); 

      services.AddMvc(); 

      var skipSSL = Configuration.GetValue<bool>("LocalTest:skipSSL"); 
      // requires using Microsoft.AspNetCore.Mvc; 
      services.Configure<MvcOptions>(options => 
      { 
       // Set LocalTest:skipSSL to true to skip SSL requrement in 
       // debug mode. This is useful when not using Visual Studio. 
       if (!skipSSL) 
       { 
        options.Filters.Add(new RequireHttpsAttribute()); 
       } 
      }); 


      services.AddMvc(config => 
      { 
       var policy = new AuthorizationPolicyBuilder() 
           .RequireAuthenticatedUser() 
           .Build(); 
       config.Filters.Add(new AuthorizeFilter(policy)); 
      }); 

      services.AddScoped<IAuthorizationHandler, 
         OwnerRaceAuthorizationHandler>(); 

      services.AddSingleton<IAuthorizationHandler, 
            AdminRaceAuthorizationHandler>(); 

      services.AddScoped<IAuthorizationHandler, 
         OwnerRaceEntriesAuthorizationHandler>(); 

      services.AddSingleton<IAuthorizationHandler, 
            AdminRaceEntriesAuthorizationHandler>(); 

     } 

     // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 
     public void Configure(IApplicationBuilder app, IHostingEnvironment env) 
     { 
      if (env.IsDevelopment()) 
      { 
       app.UseDeveloperExceptionPage(); 
       app.UseBrowserLink(); 
       app.UseDatabaseErrorPage(); 
      } 
      else 
      { 
       app.UseExceptionHandler("/Home/Error"); 
      } 

      app.UseStaticFiles(); 

      app.UseAuthentication(); 

      app.UseMvc(routes => 
      { 
       routes.MapRoute(
        name: "default", 
        template: "{controller=Home}/{action=Index}/{id?}"); 
      }); 

      // Enable middleware to serve generated Swagger as a JSON endpoint. 
      app.UseSwagger(); 

      // Enable middleware to serve swagger-ui (HTML, JS, CSS etc.), specifying the Swagger JSON endpoint. 
      app.UseSwaggerUI(c => 
      { 
       c.SwaggerEndpoint("/swagger/v1/swagger.json", "Techsportise API V1"); 
      }); 


     } 
    } 
} 

AKTUALISIERT startup.cs Veränderungen Kommentare zu reflektieren.

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Threading.Tasks; 
using Microsoft.AspNetCore.Builder; 
using Microsoft.AspNetCore.Identity; 
using Microsoft.EntityFrameworkCore; 
using Microsoft.AspNetCore.Hosting; 
using Microsoft.Extensions.Configuration; 
using Microsoft.Extensions.DependencyInjection; 
using TechsportiseOnline.Data; 
using TechsportiseOnline.Models; 
using TechsportiseOnline.Services; 
using Microsoft.AspNetCore.Mvc; 
using Microsoft.AspNetCore.Authorization; 
using Microsoft.AspNetCore.Mvc.Authorization; 
using TechsportiseOnline.Authorization; 
using TechsportiseOnline.Helpers; 
using Swashbuckle.AspNetCore.Swagger; 
using System.IO; 
using Microsoft.Extensions.PlatformAbstractions; 
using static TechsportiseOnline.Helpers.Swagger; 
using Microsoft.AspNetCore.Authentication.JwtBearer; 
using Microsoft.IdentityModel.Tokens; 
using System.Text; 

namespace TechsportiseOnline 
{ 
    public class Startup 
    { 
     public Startup(IConfiguration configuration) 
     { 
      Configuration = configuration; 
     } 

     public IConfiguration Configuration { get; } 

     // This method gets called by the runtime. Use this method to add services to the container. 
     public void ConfigureServices(IServiceCollection services) 
     { 
      services.AddDbContext<ApplicationDbContext>(options => 
       options.UseSqlServer(Configuration.GetConnectionString("TechsportiseDB"))); 
                 //options.UseInMemoryDatabase("Teschsportise")); 

      services.AddIdentity<ApplicationUser, IdentityRole>(config => 
       { 
        config.SignIn.RequireConfirmedEmail = true; 
       }) 
       .AddEntityFrameworkStores<ApplicationDbContext>() 
       .AddDefaultTokenProviders(); 

      services.Configure<IdentityOptions>(options => 
      { 
       // Password settings 
       options.Password.RequireDigit = true; 
       options.Password.RequiredLength = 6; 
       options.Password.RequireNonAlphanumeric = false; 
       options.Password.RequireUppercase = false; 
       options.Password.RequireLowercase = false; 
       options.Password.RequiredUniqueChars = 2; 

       // Lockout settings 
       options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(30); 
       options.Lockout.MaxFailedAccessAttempts = 10; 
       options.Lockout.AllowedForNewUsers = true; 

       // User settings 
       options.User.RequireUniqueEmail = true; 
      }); 

      services.Configure<AuthMessageSenderOptions>(Configuration); 

      //services.ConfigureApplicationCookie(options => 
      //{ 
      // // Cookie settings 
      // options.Cookie.HttpOnly = true; 
      // options.Cookie.Expiration = TimeSpan.FromDays(150); 
      // options.LoginPath = "/Account/Login"; // If the LoginPath is not set here, ASP.NET Core will default to /Account/Login 
      // options.LogoutPath = "/Account/Logout"; // If the LogoutPath is not set here, ASP.NET Core will default to /Account/Logout 
      // options.AccessDeniedPath = "/Account/AccessDenied"; // If the AccessDeniedPath is not set here, ASP.NET Core will default to /Account/AccessDenied 
      // options.SlidingExpiration = true; 
      //}); 

      // Add application services. 
      services.AddTransient<IEmailSender, Email>(); 
      //services.AddTransient<ICreateContact>(); 
      //services.AddTransient<IUpdateContact>(); 

      services.AddSwaggerGen(c => 
      { 
       c.SwaggerDoc("v1", new Info { Title = "Techsportise API", Version = "v1" }); 
       c.OperationFilter<AddRequiredHeaderParameter>(); 
       var filePath = Path.Combine(PlatformServices.Default.Application.ApplicationBasePath, "Techsportise.xml"); 
       c.IncludeXmlComments(filePath); 
      }); 

      services.Configure<JWTSettings>(Configuration.GetSection("JWTSettings")); 


      //services.AddAuthentication() 
      // .AddCookie() 
      // .AddJwtBearer(options => 
      // { 
      //  options.RequireHttpsMetadata = false; 
      //  options.IncludeErrorDetails = true; 

      //  var secretKey = Configuration.GetSection("JWTSettings:SecretKey").Value; 
      //  var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(secretKey)); 

      //  options.TokenValidationParameters = new TokenValidationParameters 
      //  { 

      //   ValidateIssuer = true, 
      //   ValidIssuer = Configuration.GetSection("JWTSettings:Issuer").Value, 
      //   ValidateAudience = true, 
      //   ValidAudience = Configuration.GetSection("JWTSettings:Audience").Value, 
      //   ValidateIssuerSigningKey = true, 
      //   IssuerSigningKey = signingKey, 

      //  }; 
      // }); 

      services.AddAuthentication() 
       .AddCookie() 
       .AddJwtBearer(options => 
       { 
        options.Audience = "xyz"; 
        options.Authority = "yzx"; 
       }); 

      services.AddMvc(); 

      var skipSSL = Configuration.GetValue<bool>("LocalTest:skipSSL"); 
      // requires using Microsoft.AspNetCore.Mvc; 
      services.Configure<MvcOptions>(options => 
      { 
       // Set LocalTest:skipSSL to true to skip SSL requrement in 
       // debug mode. This is useful when not using Visual Studio. 
       if (!skipSSL) 
       { 
        options.Filters.Add(new RequireHttpsAttribute()); 
       } 
      }); 


      services.AddMvc(config => 
      { 
       var policy = new AuthorizationPolicyBuilder() 
           .RequireAuthenticatedUser() 
           .Build(); 
       config.Filters.Add(new AuthorizeFilter(policy)); 
      }); 

      services.AddScoped<IAuthorizationHandler, 
         OwnerRaceAuthorizationHandler>(); 

      services.AddSingleton<IAuthorizationHandler, 
            AdminRaceAuthorizationHandler>(); 

      services.AddScoped<IAuthorizationHandler, 
         OwnerRaceEntriesAuthorizationHandler>(); 

      services.AddSingleton<IAuthorizationHandler, 
            AdminRaceEntriesAuthorizationHandler>(); 

     } 

     // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 
     public void Configure(IApplicationBuilder app, IHostingEnvironment env) 
     { 
      if (env.IsDevelopment()) 
      { 
       app.UseDeveloperExceptionPage(); 
       app.UseBrowserLink(); 
       app.UseDatabaseErrorPage(); 
      } 
      else 
      { 
       app.UseExceptionHandler("/Home/Error"); 
      } 

      app.UseStaticFiles(); 


      app.UseAuthentication(); 

      app.UseMvc(routes => 
      { 
       routes.MapRoute(
        name: "default", 
        template: "{controller=Home}/{action=Index}/{id?}"); 
      }); 

      // Enable middleware to serve generated Swagger as a JSON endpoint. 
      app.UseSwagger(); 

      // Enable middleware to serve swagger-ui (HTML, JS, CSS etc.), specifying the Swagger JSON endpoint. 
      app.UseSwaggerUI(c => 
      { 
       c.SwaggerEndpoint("/swagger/v1/swagger.json", "Techsportise API V1"); 
      }); 


     } 
    } 
} 

Added ein brandneues Testcontroller, kopieren Sie den Code.

using Microsoft.AspNetCore.Authentication.JwtBearer; 
using Microsoft.AspNetCore.Authorization; 
using Microsoft.AspNetCore.Mvc; 
using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Threading.Tasks; 

namespace TechsportiseOnline.Controllers 
{ 
    public class TestController : Controller 
    { 
     [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] //Based on Scheme it will auth, for cookie mention [Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme)] 
     [Route("api/[controller]")] 
     public IActionResult About() 
     { 
      ViewData["Message"] = "Your application description page."; 
      return View(); 
     } 
    } 
} 

Antwort

0

Sie meinen, ohne Login Anforderung mit gültigem Token sollte passieren? Sie können dies erreichen, indem Sie das Cookie-Authentifizierungsschema oder das JWTbearer-Schema auf der Ebene ConfigureServices() entfernen.

services.AddAuthentication( // no Authenticationschemes mentioned here ) 
       .AddCookie() //CookieAuthentication 
       .AddJwtBearer(options => 
       { 
        options.Audience = "xyz"; 
        options.Authority = "yzx"; 
       }); 

Wenn Sie mit aus gültiges Token dann hatte anmelden Sie Mvc Controller schlagen können oder Web-api-Controller ohne Seiten zu irgendwelchen Login umleitet. mögen;

 [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] //Based on Scheme it will auth, for cookie mention [Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme)] 
     public IActionResult About() 
     { 
      ViewData["Message"] = "Your application description page."; 
      return View(); 
     } 
+0

Ja, das ist was ich meine. Im Wesentlichen ist Controller A MVC und sollte nur mit Cookie authentifizieren. Controller B ist die Web-API und sollte nur mit Token authentifiziert werden. Momentan benötigt Controller B Cookie AND Token. Andernfalls leitet Controller B immer zur Anmeldung um. –

+0

Sorry, ich sollte hinzufügen, ich habe Ihre Methode versucht und leider hat es nicht funktioniert. Der Controller A musste noch angemeldet sein, bevor die Token-Authentifizierung gestartet wurde. –

+0

Es sollte für mich funktionieren und funktionieren. entferne 'JwtBearerDefaults.AuthenticationScheme' von AddAuthentication() und' services.ConfigureApplicationCookie() '. Kopieren Sie stattdessen den obigen Code und lassen Sie es mich wissen. – k11k2

0

Ich habe das Problem gefunden. Beim Vergleich mit der leeren Version der App habe ich diesen AuthorizationBuilder gefunden.

services.AddMvc(config => 
    { 
     var policy = new AuthorizationPolicyBuilder() 
         .RequireAuthenticatedUser() 
         .Build(); 
     config.Filters.Add(new AuthorizeFilter(policy)); 
    }); 

Ich habe dies hinzugefügt, während ich meiner App verschiedene Rollen hinzugefügt habe. Dies zu beheben, behebt das Problem und scheint meine App nur auf autorisierte Benutzer zu beschränken.

Verwandte Themen