1

我有一个Web应用程序,我使用.NET Core 2 MVC与个人用户帐户构建。该应用程序正常工作,并在控制器上的授权很好,只显示允许的视图等。核心2授权API控制器

这是所有的标准,建立在各种教程在线指导。它没有做任何聪明的事情,做CRUD操作的基本形式。

我想添加一些将JSON交换到此应用程序的REST端点,并授权使用JWT作为授权(承载)标头的端点。根据所有的教程,这应该是相当简单的,因为他们已经合并,但我似乎无法得到任何工作。

什么似乎发生的是,MVC授权覆盖JWTBearer授权,所以我只能访问API操作(我想要路由为/ api/{action}),当我有一个登录cookie。

  1. 我需要MVC的东西与授权单独留下。这很好。
  2. 我想在/ api/{controller}/{action}/{id}处添加API端点我不介意这是否位于同一个控制器或不同的控制器中
  3. /api上的授权应该是通过JWT不记名令牌和MVC内容通过登录cookie作为标准。然而,两者都映射到相同的用户(所有者ID)

任何人都可以指向正确的方向吗?我一直在尝试实施一个简单的GET方法3天,并且我一直打砖墙。

编辑:进一步的测试揭示了一些有趣的事情。我已经安装了Swagger来测试请求。

我已经添加了第二个控制器来处理我的API方法。这是在api/Races上。此控制器具有JWTBearerDefaults作为身份验证方案。

[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] 
[Route("api/[controller]")] 
  • 如果我不通过MVC应用程序登录,并没有承载令牌的请求,将其重定向我登录。
  • 如果我没有通过MVC应用程序登录,并使用(有效)令牌发出请求,它会重定向我登录。
  • 当我通过MVC登录并执行我的请求而没有持票人令牌时,我收到401未经授权的(预计的)
  • 当我(仍然)登录并使用有效的持票人令牌执行我的请求时,我收到有效的响应。
  • 当我仍然在执行我的一个无效的承载令牌请求登录我得到一个401未授权的(预期)

所以看起来它是使用令牌认证为授权的第二层。我希望它在/ api控制器上使用它作为授权的唯一方法。

这里是我的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"); 
      }); 


     } 
    } 
} 

修订 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() 
      // .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"); 
      }); 


     } 
    } 
} 

增加了一个全新的的TestController,复制你的代码。

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(); 
     } 
    } 
} 

回答

0

你的意思是,没有登录使用有效令牌的请求应通过?您可以通过在ConfigureServices()级别删除Cookie身份验证方案或JWTBearer方案来实现此目的。

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

如果您有有效的标记,然后用了密码,你可以打任何MVC控制器或Web API控制器不重定向到任何登录页面。喜欢;

 [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

是的,这就是我的意思。基本上,控制器A是MVC,并且只应使用Cookie进行身份验证。控制器B是Web API,只能使用令牌进行验证。目前控制器B需要Cookie和令牌。否则,控制器B总是重定向登录。 –

+0

对不起,我应该补充,我试过你的方法,不幸的是,它没有工作。在令牌认证加入之前,点击控制器A仍然要求我登录。 –

+0

它应该为我工作和工作。从AddAuthentication()和services.ConfigureApplicationCookie()中移除'JwtBearerDefaults.AuthenticationScheme'。相反,请复制上面的代码并让我知道。 – k11k2

0

我发现了这个问题。当与应用程序的空白版本比较时,我发现这个AuthorizationBuilder。

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

我在添加不同的角色到我的应用程序时添加了这个。采取这一措施可以解决问题,而且似乎仍然只能将我的应用程序限制为授权用户。