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

我在添加不同的角色到我的應用程序時添加了這個。採取這一措施可以解決問題,而且似乎仍然只能將我的應用程序限制爲授權用戶。