- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我有一个非常奇怪的问题,我花了一整天的时间进行调试,但离解决还差得很远。我正在将我的应用程序从 ASP.NET Core 1.x 升级到 2.1。作为这样做的一部分,我必须重新连接身份验证和授权机制。我们使用 JWTBearer 身份验证,我正在使用 postman 触发 API 调用,它执行管道,我可以看到 AuthHandler 正在执行。但是,如果我再次发出相同的请求,则 AuthHandler 不会执行并且调试器“跳过”“context.AuthenticateAsync”调用并返回先前的结果。为了详细说明,我编写了一个自定义身份验证处理程序,它是 JWTAuthHandler 的复制粘贴。创建自定义处理程序的代码基于 answer在这里。
using Microsoft.AspNetCore.Authentication.JwtBearer;
public class CustomAuthOptions : JwtBearerOptions
{
}
using Microsoft.AspNetCore.Authentication;
public static class CustomAuthExtensions
{
public static AuthenticationBuilder AddCustomAuth(this AuthenticationBuilder builder, Action<CustomAuthOptions> configureOptions)
{
return builder.AddScheme<CustomAuthOptions, CustomAuthHandler>("CustomScheme", configureOptions);
}
}
public class CustomAuthHandler : AuthenticationHandler<CustomAuthOptions>
{
private OpenIdConnectConfiguration _configuration;
public CustomAuthHandler(IOptionsMonitor<CustomAuthOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock)
{
}
/// <summary>
/// The handler calls methods on the events which give the application control at certain points where processing is occurring.
/// If it is not provided a default instance is supplied which does nothing when the methods are called.
/// </summary>
protected new JwtBearerEvents Events
{
get => (JwtBearerEvents)base.Events;
set => base.Events = value;
}
protected override Task<object> CreateEventsAsync() => Task.FromResult<object>(new JwtBearerEvents());
/// <summary>
/// Searches the 'Authorization' header for a 'Bearer' token. If the 'Bearer' token is found, it is validated using <see cref="TokenValidationParameters"/> set in the options.
/// </summary>
/// <returns></returns>
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
string token = null;
try
{
// Give application opportunity to find from a different location, adjust, or reject token
var messageReceivedContext = new MessageReceivedContext(Context, Scheme, Options);
// event can set the token
await Events.MessageReceived(messageReceivedContext);
if (messageReceivedContext.Result != null)
{
return messageReceivedContext.Result;
}
// If application retrieved token from somewhere else, use that.
token = messageReceivedContext.Token;
if (string.IsNullOrEmpty(token))
{
string authorization = Request.Headers["Authorization"];
// If no authorization header found, nothing to process further
if (string.IsNullOrEmpty(authorization))
{
return AuthenticateResult.NoResult();
}
if (authorization.StartsWith("CustomAuth ", StringComparison.OrdinalIgnoreCase))
{
token = authorization.Substring("CustomAuth ".Length).Trim();
}
// If no token found, no further work possible
if (string.IsNullOrEmpty(token))
{
return AuthenticateResult.NoResult();
}
}
if (_configuration == null && Options.ConfigurationManager != null)
{
_configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.RequestAborted);
}
var validationParameters = Options.TokenValidationParameters.Clone();
if (_configuration != null)
{
var issuers = new[] { _configuration.Issuer };
validationParameters.ValidIssuers = validationParameters.ValidIssuers?.Concat(issuers) ?? issuers;
validationParameters.IssuerSigningKeys = validationParameters.IssuerSigningKeys?.Concat(_configuration.SigningKeys)
?? _configuration.SigningKeys;
}
List<Exception> validationFailures = null;
SecurityToken validatedToken;
foreach (var validator in Options.SecurityTokenValidators)
{
if (validator.CanReadToken(token))
{
ClaimsPrincipal principal;
try
{
principal = validator.ValidateToken(token, validationParameters, out validatedToken);
}
catch (Exception ex)
{
////Logger.TokenValidationFailed(ex);
// Refresh the configuration for exceptions that may be caused by key rollovers. The user can also request a refresh in the event.
if (Options.RefreshOnIssuerKeyNotFound && Options.ConfigurationManager != null
&& ex is SecurityTokenSignatureKeyNotFoundException)
{
Options.ConfigurationManager.RequestRefresh();
}
if (validationFailures == null)
{
validationFailures = new List<Exception>(1);
}
validationFailures.Add(ex);
continue;
}
////Logger.TokenValidationSucceeded();
var tokenValidatedContext = new TokenValidatedContext(Context, Scheme, Options)
{
Principal = principal,
SecurityToken = validatedToken
};
await Events.TokenValidated(tokenValidatedContext);
if (tokenValidatedContext.Result != null)
{
return tokenValidatedContext.Result;
}
if (Options.SaveToken)
{
tokenValidatedContext.Properties.StoreTokens(new[]
{
new AuthenticationToken { Name = "access_token", Value = token }
});
}
tokenValidatedContext.Success();
return tokenValidatedContext.Result;
}
}
if (validationFailures != null)
{
var authenticationFailedContext = new AuthenticationFailedContext(Context, Scheme, Options)
{
Exception = (validationFailures.Count == 1) ? validationFailures[0] : new AggregateException(validationFailures)
};
await Events.AuthenticationFailed(authenticationFailedContext);
if (authenticationFailedContext.Result != null)
{
return authenticationFailedContext.Result;
}
return AuthenticateResult.Fail(authenticationFailedContext.Exception);
}
return AuthenticateResult.Fail("No SecurityTokenValidator available for token: " + token ?? "[null]");
}
catch (Exception ex)
{
////Logger.ErrorProcessingMessage(ex);
var authenticationFailedContext = new AuthenticationFailedContext(Context, Scheme, Options)
{
Exception = ex
};
await Events.AuthenticationFailed(authenticationFailedContext);
if (authenticationFailedContext.Result != null)
{
return authenticationFailedContext.Result;
}
throw;
}
}
protected override async Task HandleChallengeAsync(AuthenticationProperties properties)
{
var authResult = await HandleAuthenticateOnceSafeAsync();
var eventContext = new JwtBearerChallengeContext(Context, Scheme, Options, properties)
{
AuthenticateFailure = authResult?.Failure
};
// Avoid returning error=invalid_token if the error is not caused by an authentication failure (e.g missing token).
if (Options.IncludeErrorDetails && eventContext.AuthenticateFailure != null)
{
eventContext.Error = "invalid_token";
eventContext.ErrorDescription = CreateErrorDescription(eventContext.AuthenticateFailure);
}
await Events.Challenge(eventContext);
if (eventContext.Handled)
{
return;
}
Response.StatusCode = 401;
if (string.IsNullOrEmpty(eventContext.Error) &&
string.IsNullOrEmpty(eventContext.ErrorDescription) &&
string.IsNullOrEmpty(eventContext.ErrorUri))
{
Response.Headers.Append(HeaderNames.WWWAuthenticate, Options.Challenge);
}
else
{
// https://tools.ietf.org/html/rfc6750#section-3.1
// WWW-Authenticate: Bearer realm="example", error="invalid_token", error_description="The access token expired"
var builder = new StringBuilder(Options.Challenge);
if (Options.Challenge.IndexOf(" ", StringComparison.Ordinal) > 0)
{
// Only add a comma after the first param, if any
builder.Append(',');
builder.Append(',');
}
if (!string.IsNullOrEmpty(eventContext.Error))
{
builder.Append(" error=\"");
builder.Append(eventContext.Error);
builder.Append("\"");
}
if (!string.IsNullOrEmpty(eventContext.ErrorDescription))
{
if (!string.IsNullOrEmpty(eventContext.Error))
{
builder.Append(",");
}
builder.Append(" error_description=\"");
builder.Append(eventContext.ErrorDescription);
builder.Append('\"');
}
if (!string.IsNullOrEmpty(eventContext.ErrorUri))
{
if (!string.IsNullOrEmpty(eventContext.Error) ||
!string.IsNullOrEmpty(eventContext.ErrorDescription))
{
builder.Append(",");
}
builder.Append(" error_uri=\"");
builder.Append(eventContext.ErrorUri);
builder.Append('\"');
}
Response.Headers.Append(HeaderNames.WWWAuthenticate, builder.ToString());
}
}
private static string CreateErrorDescription(Exception authFailure)
{
IEnumerable<Exception> exceptions;
if (authFailure is AggregateException agEx)
{
exceptions = agEx.InnerExceptions;
}
else
{
exceptions = new[] { authFailure };
}
var messages = new List<string>();
foreach (var ex in exceptions)
{
// Order sensitive, some of these exceptions derive from others
// and we want to display the most specific message possible.
switch (ex)
{
case SecurityTokenInvalidAudienceException _:
messages.Add("The audience is invalid");
break;
case SecurityTokenInvalidIssuerException _:
messages.Add("The issuer is invalid");
break;
case SecurityTokenNoExpirationException _:
messages.Add("The token has no expiration");
break;
case SecurityTokenInvalidLifetimeException _:
messages.Add("The token lifetime is invalid");
break;
case SecurityTokenNotYetValidException _:
messages.Add("The token is not valid yet");
break;
case SecurityTokenExpiredException _:
messages.Add("The token is expired");
break;
case SecurityTokenSignatureKeyNotFoundException _:
messages.Add("The signature key was not found");
break;
case SecurityTokenInvalidSignatureException _:
messages.Add("The signature is invalid");
break;
}
}
return string.Join("; ", messages);
}
}
然后是 Startup.cs 来连接它:
public class Startup
{
/// <summary>
/// Initializes a new instance of the <see cref="Startup"/> class.
/// </summary>
/// <param name="configuration">The configuration.</param>
public Startup(IConfiguration configuration)
{
this.Configuration = configuration;
ConfigureLogging();
}
/// <summary>
/// Gets the configuration.
/// </summary>
/// <value>
/// The configuration.
/// </value>
public IConfiguration Configuration { get; }
/// <summary>
/// Gets or sets the Container
/// </summary>
private IUnityContainer Container { get; set; }
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.Build();
public IServiceProvider ConfigureServices(IServiceCollection services)
{
var logger = Logger.For(this).ForAction(nameof(ConfigureServices));
services.Configure<GzipCompressionProviderOptions>(options => options.Level = CompressionLevel.Optimal);
services.AddResponseCompression();
logger.Info("Configuring JWT Bearer Token Authorization...");
services.AddAuthentication(options =>
{
// the scheme name has to match the value we're going to use in AuthenticationBuilder.AddScheme(...)
options.DefaultAuthenticateScheme = "CustomScheme";
options.DefaultChallengeScheme = "CustomScheme";
})
.AddCustomAuth(options => {
options.Audience = this.Configuration.ObtainConfiguredString(ConfigurationKeys.ValidAudienceId);
options.Authority = this.Configuration.ObtainConfiguredString(ConfigurationKeys.IssuerId);
options.SaveToken = false;
options.TokenValidationParameters = new TokenValidationParameters().WithConfiguredParameters(this.Configuration);
});
logger.Info("Adding Authorization policies to Services...");
services.AddAuthorization(
options =>
{
options.DefaultPolicy = new AuthorizationPolicyBuilder("CustomScheme").RequireAuthenticatedUser().Build();
});
services.AddTransient<IHttpContextAccessor, HttpContextAccessor>();
services.AddTransient<IAuthenticationHandler, CustomAuthHandler>();
EnableCors(services);
logger.Info("Adding MVC support to Services...");
services.AddMvc(config =>
{
var defaultPolicy = new AuthorizationPolicyBuilder(new[] { "CustomScheme" })
.RequireAuthenticatedUser()
.Build();
config.Filters.Add(new AuthorizeFilter(defaultPolicy));
});
Container = new UnityContainer();
logger.Info("Registering other Services with UnityContainer...");
Container.RegisterServices(Configuration);
// Configure Microsoft DI for Unity resolution
logger.Info("Configuring ASP.Net Core service resolution to use UnityContainer...");
return services.UseUnityResolution(Container, s => s.BuildServiceProvider());
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
/// <summary>
/// The Configure
/// </summary>
/// <param name="app">The app<see cref="IApplicationBuilder"/></param>
/// <param name="env">The env<see cref="IHostingEnvironment"/></param>
/// <param name="loggerFactory">The loggerFactory<see cref="ILoggerFactory"/></param>
/// <param name="memoryCache">The memoryCache<see cref="IMemoryCache"/></param>
/// <param name="contextAccessor">The contextAccessor<see cref="IHttpContextAccessor"/></param>
/// <param name="authzClient">The authzClient<see cref="IAuthzClient"/></param>
public void Configure(
IApplicationBuilder app,
IHostingEnvironment env,
ILoggerFactory loggerFactory,
IMemoryCache memoryCache,
IHttpContextAccessor contextAccessor,
IAuthzClient authzClient)
{
var logger = Logger.For(this).ForAction(nameof(Configure));
logger.Info("Configuring ASP.Net Core logging framework...");
loggerFactory.AddConsole(this.Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
var corsEnabled = this.Configuration.ObtainConfiguredBooleanWithDefault(ConfigurationKeys.EnableCors, false);
if (corsEnabled)
{
app.UseCors("CorsPolicy");
}
logger.Info("Configuring ASP.Net Core custom status page...");
app.UseStatusCodePagesWithReExecute("/error/{0}");
if (env.IsDevelopment())
{
logger.Info("Configuring development middle-ware...");
app.UseDeveloperExceptionPage();
app.UseBrowserLink();
}
logger.Info("Configuring standard ASP.Net Core behaviors...");
app.UseDefaultFiles();
app.UseStaticFiles();
////app.UseAuthentication();
app.Use(async (context, next) =>
{
if (!context.User.Identity.IsAuthenticated)
{
var result = await context.AuthenticateAsync("CustomScheme");
if (result?.Principal != null)
{
context.User = result.Principal;
}
}
await next.Invoke();
});
app.UseMvc();
app.WithRequestLogging();
}
private void EnableCors(IServiceCollection service)
{
var logger = Logger.For(this).ForAction(nameof(EnableCors));
var corsEnabled = this.Configuration.ObtainConfiguredBooleanWithDefault(ConfigurationKeys.EnableCors, false);
if (corsEnabled)
{
logger.Verbose("Configuring ASP.Net Core CORS support...");
service.AddCors(
options =>
{
options.AddPolicy("CorsPolicy",
builder =>
{
builder.AllowAnyOrigin();
builder.AllowAnyHeader();
builder.AllowAnyMethod();
builder.AllowCredentials();
});
});
}
}
}
}
有人可以告诉我我做错了什么吗?第一次使用正确的 AuthorizationHeader 和访问 token 触发 postman 请求时,此行执行 CustomAuthHandler:
var result = await context.AuthenticateAsync("CustomScheme");
然而,调试器第二次越过该代码?这让我难以自拔。我一定是遗漏了一些基本的东西!
编辑:在 Core 1.x 版本中,ConfigureServices 设置为
public IServiceProvider ConfigureServices(IServiceCollection services)
{
var logger = Logger.For(this).ForAction(nameof(ConfigureServices));
logger.Verbose("Adding MVC support to Services...");
// Add framework services.
services.AddMvc();
logger.Verbose("Adding Authorization policies to Services...");
services.AddAuthorization(
options =>
{
options.AddPolicy(
"SomePermission",
policy => policy.RequireClaim("claimUrl", "Some Permission"));
});
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
container = new UnityContainer();
logger.Verbose("Registering other Services with UnityContainer...");
container.RegisterServices(Configuration);
// Configure Microsoft DI for Unity resolution
logger.Verbose("Configuring ASP.Net Core service resolution to use UnityContainer...");
return services.UseUnityResolution(container, s => s.BuildServiceProvider());
}
Configure() 的连接方式如下
app.UseAuth0JwtBearerAuthentication(
new JwtBearerOptions
{
AutomaticAuthenticate = true,
AutomaticChallenge = true,
TokenValidationParameters =
new TokenValidationParameters().WithConfiguredParameters(this.Configuration)
});
if (env.IsDevelopment())
{
logger.Verbose("Configuring development middleware...");
app.UseDeveloperExceptionPage();
app.UseBrowserLink();
}
logger.Verbose("Configuring standard ASP.Net Core behaviors...");
app.UseDefaultFiles();
app.UseMvc();
app.UseStaticFiles();
如果我执行 postman 调用,则使用此版本,然后我会为每个请求获得一个新的 ClaimsPrincipal。那么 ASP.NET Core 2.1 中发生了什么变化?
最佳答案
对于面临相同问题的任何人;我的问题原来是Unity。 ASP.NET Core 2.0 不支持开箱即用的 Unity,因为 ConfigureServices()
Startup.cs
中的方法是第三方 DI 容器的替代品,如 Unity 或 Autofac。但是,如果您仍想使用 Unity,则需要 Nuget Unity.Microsoft.DependencyInjection到你的项目。 Github 存储库包含有关如何连接它的详细信息。
此外,所有其他相关项目都使用 Unity 4.0.1,它在 Microsoft.Practices.Unity
下有 IUnityContainer。 ,而从 Unity 5 开始,IUnityContainer 已移至 Unity
命名空间。这是一个额外的陷阱,即使在按照 Github 存储库的说明设置 DI 容器之后,我也会遇到依赖项解析失败的异常。解决方法是使用 Microsoft.Practices.Unity
创建一个新的 UnityContainer ,让依赖项目引导它,然后将这些注册复制到 Unity
下的 IUnityContainer命名空间。
启动.cs
public void ConfigureContainer(IUnityContainer container)
{
container.RegisterServices(Configuration);
}
UnityRegistrations.cs
public static void RegisterServices(this IUnityContainer container, IConfiguration configuration)
{
// Microsoft.Practices.Unity
var currentContainer = new UnityContainer();
// Bootstrap this and register dependencies
// Then copy them over
foreach (var registration in currentContainer.Registrations)
{
container.RegisterType(registration.RegisteredType, registration.MappedToType);
}
}
关于c# - ASP.NET Core 2.0 app.UseAuthentication 未针对每个请求执行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50647513/
我试图对 ASP.Net MVC 有一个高层次的理解,我开始意识到它看起来很像原始的 ASP 脚本。过去,我们将“模型”/业务逻辑代码组织到 VBScript 类或 VB COM 组件中。 当然,现在
我已经搜索了一段时间,但似乎找不到答案。 我想在我的旋转木马中显示一个计数器,左边是当前项目(工作),左边是项目总数。 我的代码:
. 最佳答案 Scott Gu 称这些为代码块。这就是我的看法。 http://weblogs.asp.net/scottgu/archive/2010/04/06/new-lt-gt-syntax
我有一个使用 Visual Studio 2010/.net 4/VB 制作的网站。 我真的很喜欢我发现的 FAQ 系统的布局,因为它很简单,但它是经典的 asp。所以,显然,我不能包括我的母版页布局
好吧,对于你们许多人来说,这个问题可能有一个非常明显的答案,但它让我难住了。 我有一个 asp.net Web 表单,上面有两个控件(嗯,不止这两个,但我们将重点关注这些) - 第一个是 asp:dr
当我将 ASP.NET 复选框控件设置为 asp.net 更新面板的异步回发触发器时,EventName 属性是什么? 最佳答案 我相信它是 CheckedChanged。 关于asp.net - a
我有一个用经典 asp 编写的(巨大的)网站。现在我必须切换到 vb.net (razor)。有没有办法将这两个结合起来直到切换完成? 有没有办法让应用程序与经典的 asp 和 vb.net 一起工作
I am creating a products page, where the user selects an option in a radiobuttonlist for example, an
我最近将一个经典的 ASP 应用程序转换为 ASP.NET 3.5,但我觉得我的经典 ASP 版本要快一些(我不知道可能买家会后悔)。 所以你们能帮我解决这个问题吗,让我知道哪个更快,asp、asp.
从本周开始,我被要求开始学习如何使用 ASP 开发网站。我通过 XNA 对 C# 有一定的经验,所以这部分对我来说并不是什么麻烦。 我一直在关注Music Store Tutorial这需要我设置一个
关闭。这个问题需要多问focused 。目前不接受答案。 想要改进此问题吗?更新问题,使其仅关注一个问题 editing this post . 已关闭 8 年前。 Improve this ques
我想将一些表单变量发布到经典 ASP 页面中。我不想改变经典的 ASP 页面,因为需要完成大量的工作,以及消耗它们的页面数量。 经典的 ASP 页面需要将表单变量 Username 和 Userpas
已结束。此问题正在寻求书籍、工具、软件库等的推荐。它不满足Stack Overflow guidelines 。目前不接受答案。 我们不允许提出寻求书籍、工具、软件库等推荐的问题。您可以编辑问题,以便
在某种程度上,这可能是一个异端问题。我们有一个大型站点,其中许多页面仍在ASP中。通常,并没有真正动态的,而是包括(通过SSI或Server.Execute)定期重新生成的HTML块。看起来好像是一个
关闭。这个问题需要多问focused 。目前不接受答案。 想要改进此问题吗?更新问题,使其仅关注一个问题 editing this post . 已关闭 9 年前。 Improve this ques
我有一个遗留的 ASP 应用程序——在不久的某个时候——需要迁移到 ASP.Net 2.0(以与也在 2.0 中的其他应用程序兼容)。 对于这类事情是否有最佳实践,即作为第一步将当前 html、vbs
我目前在一家公司工作,该公司使用 ASP.NET Webforms 和旧 ASP 页面的组合进行 Web 开发。这对于他们当前的项目来说效果很好,但我想说服/建议他们切换到 ASP.NET MVC,因
我有一个经典的 asp 应用程序。我想将该页面的竞赛表格发布到 Asp.Net 表格。原因是我想在进入数据库之前使用我在 Asp.Net 页面中内置的大量逻辑进行验证,而我对 asp 不太了解。更不用
我知道在 ASP.NET MVC 中,您可以拥有移动 View 并执行类似 Index.mobile.cshtml 的操作。和 _Layout.mobile.cshtml并且服务器知道将这些 View
我需要从一些服务器端 c#.net 代码中调用经典 asp 页面上的 VBscript 函数 - 有谁知道一种干净的方法来做到这一点?在 .net 中重写函数不是一种选择。 我会再解释一下这个问题..
我是一名优秀的程序员,十分优秀!