- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我有两个问题,都引用下面的代码:
为什么在我调用 authenticateResult = await context.AuthenticateAsync(); 后 authenticateResult.Succeeded 为 false?
为什么我需要从我的自定义中间件 InvokeAsync 方法调用“return”才能正常工作?
我有一个使用 OpenIdConnect 的 asp.net 核心应用程序。该应用程序有两个 Controller 操作;它们都具有 [Authorize] 属性,因此当应用程序启动时,用户会自动进入 OpenIdConnect 过程。这很好用。
下面是我如何配置我的 OpenIdConnect 中间件,我碰巧使用的是 PingOne:
services.AddAuthentication(authenticationOptions =>
{
authenticationOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
authenticationOptions.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(openIdConnectOptions =>
{
openIdConnectOptions.Authority = Configuration["PingOne:Authority"];
openIdConnectOptions.CallbackPath = Configuration["PingOne:CallbackPath"];
openIdConnectOptions.ClientId = Configuration["PingOne:ClientId"];
openIdConnectOptions.ClientSecret = Configuration["PingOne:ClientSecret"];
openIdConnectOptions.ResponseType = Configuration["PingOne:ResponseType"];
openIdConnectOptions.Scope.Clear();
foreach (var scope in scopes.GetChildren())
{
openIdConnectOptions.Scope.Add(scope.Value);
}
});
在用户进行身份验证后,我立即将用户重定向到另一个网站(使用相同的 OpenIdConnect 身份验证)。在“OtherWebsite”上,用户选择各种选项,然后被重定向回“OriginalWebsite”到一个名为“ReturningFromOtherWebsite”的特殊路径。返回到 OriginalWebSite 时,我读取了查询字符串,根据查询字符串将一些声明加载到用户的主体身份中,并设置一个 session 变量,这样我就知道我已经访问过 OtherWebSite 一次。
我在 OriginalWebSite 中实际上没有名为“ReturningFromOtherWebsite”的 Controller 方法,因此我需要在我的中间件中查找该路径并拦截对它的处理。
我决定将此功能包装在我称为“AfterAuthenticationMiddleware”的自定义中间件中,它看起来像这样。我的问题由以“//QUESTION:...”开头的评论标记
public class AfterAuthenticationMiddleware
{
private readonly RequestDelegate _next;
private readonly IConfiguration Configuration;
private IMembershipRepository MembershipRepository;
public AfterAuthenticationMiddleware(RequestDelegate next,
IConfiguration configuration)
{
_next = next;
Configuration = configuration;
}
private void SignInWithSelectedIdentity(Guid userId,
ClaimsIdentity claimsIdentity,
AuthenticateResult authenticateResult,
HttpContext context)
{
string applicationName = Configuration["ApplicationName"];
List<string> roles = MembershipRepository.GetRoleNamesForUser(userId, applicationName);
foreach (var role in roles)
{
claimsIdentity.AddClaim(new Claim(ClaimTypes.Role, role));
}
//add the claim to the authentication cookie
context.SignInAsync(authenticateResult.Principal, authenticateResult.Properties);
}
public async Task InvokeAsync(HttpContext context,
IMembershipRepository membershipRepository)
{
MembershipRepository = membershipRepository;
bool isIdentitySelected = context.Session.GetBoolean("IsIdentitySelected").GetValueOrDefault();
if (isIdentitySelected)
{
//I know from existence of Session variable that there is no work to do here.
await _next(context);
return;
}
var authenticateResult = await context.AuthenticateAsync();
ClaimsIdentity claimsIdentity = null;
//the Controller action ReturningFromOtherWebSite does not actually exist.
if (context.Request.Path.ToString().Contains("ReturningFromOtherWebSite"))
{
if (!authenticateResult.Succeeded)
{
//this next line triggers the OpenIdConnect process
await context.ChallengeAsync();
//QUESTION: If I re-fetch the authenticateResult here, why is IsSucceeded false, for example:
//var authenticateResult = await context.AuthenticateAsync();
//QUESTION: why is the next line needed for this to work
return;
}
claimsIdentity = (ClaimsIdentity)authenticateResult.Principal.Identity;
//set the Session variable so that on future requests we can bail out of this method quickly.
context.Session.SetBoolean(Constants.IsIdentitySelected, true);
var request = context.Request;
//load some claims based on what the user selected in "OtherWebSite"
string selectedIdentity = request.Query["selectedIdentity"];
if (!Guid.TryParse(selectedIdentity, out Guid userId))
{
throw new ApplicationException(
$"Unable to parse Guid from 'selectedIdentity':{selectedIdentity} ");
}
SignInWithSelectedIdentity(userId, claimsIdentity, authenticateResult, context);
//redirect user to the page that the user originally requested
string returnUrl = request.Query["returnUrl"];
if (string.IsNullOrEmpty(returnUrl))
throw new ApplicationException(
$"Request is ReturnFromIdentityManagement but missing required parameter 'returnUrl' in querystring:{context.Request.QueryString} ");
string path = $"{request.Scheme}://{request.Host}{returnUrl}";
Log.Logger.Verbose($"AfterAuthentication InvokeAsync Redirect to {path}");
context.Response.Redirect(path);
//I understand why I call "return" here; I just want to send the user on to the page he/she originally requested without any more middleware being invoked
return;
}
if (!authenticateResult.Succeeded)
{
//if the user has not gone through OIDC there is nothing to do here
await _next(context);
return;
}
//if get here it means user is authenticated but has not yet selected an identity on OtherWebSite
claimsIdentity = (ClaimsIdentity)authenticateResult.Principal.Identity;
Log.Logger.Verbose($"AfterAuthentication InvokeAsync check if redirect needed.");
var emailClaim = claimsIdentity.Claims.FirstOrDefault(o => o.Type == ClaimTypes.Email);
if(emailClaim == null)
throw new ApplicationException($"User {authenticateResult.Principal.Identity.Name} lacks an Email claim");
string emailAddress = emailClaim.Value;
if(string.IsNullOrWhiteSpace(emailAddress))
throw new ApplicationException("Email claim value is null or whitespace.");
string applicationName = Configuration["ApplicationName"];
if(string.IsNullOrEmpty(applicationName))
throw new ApplicationException("ApplicationName missing from appsettings.json.");
//if there is just one userid associated with the email address, load the claims. if there is
//more than one the user must redirect to OtherWebSite and select it
List<Guid?> userIds =
MembershipRepository.IsOtherWebsiteRedirectNeeded(emailAddress, applicationName);
if (userIds == null
|| userIds[0] == null
|| userIds.Count > 1)
{
//include the path the user was originally seeking, we will redirect to this path on return
//cannot store in session (we lose session on the redirect to other web site)
string queryString =
$"emailAddress={emailAddress}&applicationName={applicationName}&returnUrl={context.Request.Path}";
context.Response.Redirect($"https://localhost:44301/Home/AuthenticatedUser?{queryString}");
}
else
{
SignInWithSelectedIdentity(userIds[0].Value, claimsIdentity, authenticateResult, context);
}
await _next(context);
}
}
然后我以通常的方式在 Configure 方法中添加中间件:
app.UseAuthentication();
app.UseAfterAuthentication();
app.UseAuthorization();
出于绝望,我添加了“return”调用并震惊地发现它解决了问题,但在我知道为什么它解决了问题之前我不会感到舒服。
最佳答案
我将冒险猜测正在发生的事情。
我在 Configure() 方法的末尾将一个监听器连接到 OpenIdConnect 库,如下所示:
IdentityModelEventSource.Logger.LogLevel = EventLevel.Verbose;
IdentityModelEventSource.ShowPII = true;
var listener = new MyEventListener();
listener.EnableEvents(IdentityModelEventSource.Logger, EventLevel.Verbose);
listener.EventWritten += Listener_EventWritten;
然后在 Listener_EventWritten 事件中我正在记录数据库。
private void Listener_EventWritten(object sender, EventWrittenEventArgs e)
{
foreach (object payload in e.Payload)
{
Log.Logger.Information($"[{e.EventName}] {e.Message} | {payload}");
}
}
我还在整个应用程序中添加了详细的日志记录,以了解正在发生的事情。不幸的是,似乎没有任何方法可以将监听器附加到身份验证或授权中间件。
这是我认为正在发生的事情。每个 asp.net core 中间件按顺序触发——在请求期间按正向顺序触发,然后在响应期间按反向顺序触发。当我在我的自定义中间件中遇到让我困惑的代码时:
if (context.Request.Path.ToString().Contains("ReturningFromOtherWebSite"))
{
if (!authenticateResult.Succeeded)
{
//this next line triggers the OpenIdConnect process
await context.ChallengeAsync();
//QUESTION: If I re-fetch the authenticateResult here, why is IsSucceeded false, for example:
//var authenticateResult = await context.AuthenticateAsync();
//QUESTION: why is the next line needed for this to work
return;
}
调用“await context.ChallengeAsync();”触发身份验证中间件;我可以从日志中看到,此时 Oidc 和 Cookie 身份验证都已触发。此调用后需要“返回”,因为我不希望执行线程在我的自定义中间件中继续;相反,我想让调用“await context.ChallengeAsync();”完成它的工作并再次调用我的自定义中间件。
我可以从日志中看到我的自定义中间件确实再次被调用,并且这次 authenticateResult.Succeeded 为 true。
调用 var "authenticateResult = await context.AuthenticateAsync();"产生 false 的“成功”,因为我的自定义中间件此时“不知道”用户已通过身份验证。我的自定义中间件“知道”这一点的唯一方法是当身份验证中间件使用“await(next)”调用它时。这意味着我需要返回并等待该调用。
同样,这是我的猜测,如果有人确切知道,我将不胜感激更好的解释。我曾尝试查看 Oidc 源代码,但我承认我发现它令人困惑,因为我是 Core 的新手并且还没有完全掌握整个异步业务。
关于c# - 在 asp.net core 中,为什么 await context.ChallengeAsync() 没有按预期工作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59379370/
我正在尝试加载外部 SVG 并将其附加到 Electron 项目中的现有 SVG。 d3.xml 方法对我不起作用,所以我正在查看 d3.symbols ,希望如果我提供路径数据(来自 fs.read
我正在编写一个 Web 应用程序,使用 Go 作为后端。我正在使用这个 GraphQL 库 (link)和 Echo Web 框架 (link) .问题在于 graphql-go 库在 Go 中使用了
有没有办法改造 gin.Context至 context.Context在围棋?构建 Go 微服务应该使用什么? 最佳答案 标准库的 context.Context type 是一个接口(interf
如果我能够像这样注册一个接收器: LocalBroadcastManager.getInstance(this).registerReceiver(mMessageReceiver, new Inte
如果我有 appengine.Context 而不是 ,我不知道如何调用 cloud.WithContext 和 google.DefaultClient >上下文。上下文。 有(旧的)“appeng
有什么区别- AccountManager accountManager = (AccountManager) context.getSystemService(Context.ACCOUNT_SER
我刚读了这篇文章:Build You Own Web Framework In Go为了在处理程序之间共享值,我选择了 context.Context我通过以下方式使用它在处理程序和中间件之间共享值:
在 Visual Studio Code 中,我对 3 个“上下文”菜单项感到非常困惑:Run Tests in Context和 Debug Tests in Context和 Run .NET C
我正在使用带有 和 @Autowired 的 Spring 2.5.6 版本。 虽然我在调度程序上下文中使用 SimpleUrlHandlerMapping ,但一切正常 - Autowiring 工
我使用的是 Context.registerReceiver()、Context.sendBroadcast(Intent) 和 Context.unregisterReceiver() 但是当我看到
问题在于以下错误, [错误] 在 scala.tools.nsc.typechecker.Typers$Typer.typedApply$1(Typers.scala:4580)[错误] 在 scal
最近我正在尝试使用 SoundPool 在我的应用程序中播放一些简单的音效 但不幸的是它在 AVD 中不起作用并且应用程序崩溃 “上下文”到底是什么意思? 完全不懂 提前致谢 最佳答案 任何上下文都允
我正在使用上下文建议器,我想知道我们是否可以设置用于建议的上下文范围,而不是使用所有上下文。 目前查询需要匹配所有上下文。我们能否在上下文中添加“或”运算和/或指定用于特定查询的上下文? 以here为
我被一个使用这种方法的函数卡住了。所以我知道如何使用 expressionValue(with:context:) 函数,但上下文如何参与对我来说仍然是不透明的。也许有人有简单的例子? try tra
我正在尝试在上下文管理器中更改我的 python 程序中的目录。使用 invoke.context.Context 似乎是正确的方法,从 Fabric 文档中获取并且使用常规 with os.chdi
我最近开始使用 Android Studio 处理我的 Android 项目。我注意到在 IDE 的右下角,有文本 Context: .好奇心打败了我,所以现在我正在网上搜索更多信息。我还没有找到任
假设我有这些功能: func A(ctx context.Context) { // A takes some time to process } func B(ctx context.Con
所以,我有一个 context.Context( https://golang.org/pkg/context/ ) 变量,有没有办法列出这个变量包含的所有键? 最佳答案 可以使用不安全反射列出 co
我正在尝试找出传播 context.Context 的正确方法用于在使用 Gin 时使用 OpenTelemetry 进行跟踪。 我目前有一个 gin调用函数并传递 *gin.Context 的处理程
我们可以使用 Remove["context`*"] 删除特定上下文中的所有符号。 .但是是否可以删除 "context`"自己从系统中删除,以便它不再在 Contexts[] 中列出? 最佳答案 据
我是一名优秀的程序员,十分优秀!