我正在尝试迁移此项目 {。{3}}从.net核心1.1到2.0,但在成功登录后出现问题。登录后,我的GET api呼叫e。 G。到https://github.com/asadsahi/AspNetCoreSpa以重定向(302)结束,我不知道为什么。我收到了一个持有人令牌,看起来很好。
请求标头格式: 授权:持票人[代币]
[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" });
});
}
我的IServiceCollection-Extensions:
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;
}
这是我的包裹:
<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>
以下是我的日志:
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
我想知道以下几行:
用户授权失败:( null)
已经找到了https://localhost:44331/api/profile/test 但是还没有答案,我认为这是一个.NET Core 1问题。
答案 0 :(得分:5)
我遇到了同样的问题,为了解决问题,我必须在控制器的Authorize属性中包含身份验证方案。
在你的情况下:
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[ServiceFilter(typeof(ApiExceptionFilter))]
[ResponseCache(Location = ResponseCacheLocation.None, NoStore = true)]
public class BaseController : Controller
{
public BaseController()
{
}
}
答案 1 :(得分:4)
当您调用AddIdentity时,它会添加Cookie身份验证,该身份验证会覆盖您预期的JWT承载身份验证。解决此问题的一种方法是在设置JWT身份验证之前移动AddIdentity调用。以下是适用于我的代码:
// 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,
...
另一种选择是使用AddIdentityCore,但我从未尝试过。
答案 2 :(得分:0)
对我而言,它与跨源资源共享(CORS)有关。我在Azure中的应用服务中启用了一个API,并启用了CORS。当我将API的调用者添加到允许的源列表中时,我得到了302消失。
我必须这样做,即使我已经在我的aspnet核心启动代码中添加了这些来源。