.NET 4.8 Owin OpenIdAuthentication:中间件在完整页面与测试页面上不返回 302

问题描述 投票:0回答:1

我正在将 OpenID 身份验证插入旧的 .NET Framework 4.8 Web 应用程序。这主要是有效的。作为测试,我创建了一个从现有页面克隆的 Claims.aspx,但几乎所有内容都被删除,并且仅显示来自 keycloak 的 OpenID 声明信息作为概念证明。

这是我当前的代码(注意:这包括一些编辑来尝试解决这个问题,但到目前为止没有一个有效)

Startup.cs:

public void ConfigureAuth(IAppBuilder app)
{
    var notificationHandlers = new OpenIdConnectAuthenticationNotifications
    {
        AuthorizationCodeReceived = async (context) => {
            // Sign in the user here
        },
        RedirectToIdentityProvider = (context) => {
            if (context.OwinContext.Request.Path.Value != "/Account/SignInWithOpenId")
            {
                context.OwinContext.Response.Redirect("/Account/Login");
                context.HandleResponse();
            }
            return Task.FromResult(0);
        }
    };

    app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
    {
        ClientId = _clientId,
        ClientSecret = _clientSecret,
        Authority = _authority,
        RedirectUri = _redirectUri,
        ResponseType = OpenIdConnectResponseType.CodeIdToken,
        Scope = OpenIdConnectScope.OpenIdProfile,
        TokenValidationParameters = new TokenValidationParameters { NameClaimType = "name", },
        AuthenticationMode = AuthenticationMode.Active,
        Notifications = new OpenIdConnectAuthenticationNotifications
        {
            AuthorizationCodeReceived = async n =>
            {
                // Exchange code for access and ID tokens
                var tokenClientOptions = new TokenClientOptions
                {
                    ClientId = _clientId,
                    ClientSecret = _clientSecret,
                    Address = $"{_authority}/protocol/openid-connect/token"

                };

                var tokenClient = new TokenClient(new HttpClient() { BaseAddress = new Uri($"{_authority}/protocol/openid-connect/token") }, tokenClientOptions);




                var client = new HttpClient();
                var tokenResponse = await client.RequestAuthorizationCodeTokenAsync(new AuthorizationCodeTokenRequest
                {
                    Address = $"{_authority}/protocol/openid-connect/token",
                    ClientId = _clientId,
                    ClientSecret = _clientSecret,
                    Code = n.Code,
                    RedirectUri = _redirectUri,
                });


                var client2 = new HttpClient();

                var userInfoResponse = await client2.GetUserInfoAsync(new UserInfoRequest
                {
                    Address = $"{_authority}protocol/openid-connect/userinfo",
                    Token = tokenResponse.AccessToken
                });

                var claims = new List<Claim>(userInfoResponse.Claims)
                  {
                    new Claim("id_token", tokenResponse.IdentityToken),
                    new Claim("access_token", tokenResponse.AccessToken)
                  };

                n.AuthenticationTicket.Identity.AddClaims(claims);
            },

            RedirectToIdentityProvider = notification =>
            {
                if (notification.ProtocolMessage.RequestType == OpenIdConnectRequestType.Logout)
                {
                    var logoutUri = $"https://{_redirectUri}/v2/logout?client_id={_clientId}";

                    var postLogoutUri = notification.ProtocolMessage.PostLogoutRedirectUri;
                    if (!string.IsNullOrEmpty(postLogoutUri))
                    {
                        if (postLogoutUri.StartsWith("/"))
                        {
                            // transform to absolute
                            var request = notification.Request;
                            postLogoutUri = request.Scheme + "://" + request.Host + request.PathBase + postLogoutUri;
                        }
                        logoutUri += $"&returnTo={ Uri.EscapeDataString(postLogoutUri)}";
                    }

                    notification.Response.Redirect(logoutUri);
                    notification.HandleResponse();
                }

                notification.OwinContext.Response.Redirect("/Account/Logiasdfn");
                notification.HandleResponse();


                return Task.FromResult(0);
            }
        },
            });

            // Set Cookies as default authentication type
            app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);

            app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);

            app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                AuthenticationType = CookieAuthenticationDefaults.AuthenticationType,
                LoginPath = new PathString("/teset.aspx"),

               

                CookieSameSite = SameSiteMode.Lax,

                // More information on why the CookieManager needs to be set can be found here: 
                // https://github.com/aspnet/AspNetKatana/wiki/System.Web-response-cookie-integration-issues
                CookieManager = new SameSiteCookieManager(new Microsoft.Owin.Host.SystemWeb.SystemWebCookieManager())
            });

        }
}

然后,我从该项目中现有 aspx 页面的克隆创建了一个简单的页面,并将其精简到最低限度以显示声明:

索赔.aspx:

<%@ Page Title="" Language="C#" MasterPageFile="~/MasterMainTest.master" AutoEventWireup="true" CodeBehind="Claims.aspx.cs" Inherits="RootNamespace.Web.Claims" %>
<%@ Import Namespace="System.Security.Claims" %>

<asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder23" runat="server">
    <h2>OpenID Connect Claims</h2>
    <dl>
        <asp:DataList runat="server" ID="dlClaims">
            <ItemTemplate>
                <dt><%# ((Claim) Container.DataItem).Type %></dt>
                <dd><%# ((Claim) Container.DataItem).Value %></dd>
            </ItemTemplate>
        </asp:DataList>
    </dl>
</asp:Content>

Claims.aspx.cs:

public partial class Claims : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!Request.IsAuthenticated)
        {
            HttpContext.Current.GetOwinContext().Authentication.Challenge();
        }

        var claims = ClaimsPrincipal.Current.Claims;
        dlClaims.DataSource = claims;
        dlClaims.DataBind();
    }
}

一旦我这样做了,它就完美了。如果客户端访问 Claims.aspx,则会发出 302 重定向,并将用户带到 Keycloak 执行授权(之前的行为是重定向到

login.aspx
以获得
Forms Authentication
)。通过身份验证后,keycloak 会重定向回 Claims.aspx,页面显示信息和 Response.IsAuthenticated = true。

我现在正在尝试将其返回到我的“真实”页面中,并将

if (!Request.IsAuthenticated) { HttpContext.Current.GetOwinContext().Authentication.Challenge(); }
添加到其
Page_Load
中,就像我在
claims.aspx.cs
中一样,期望它“正常工作”。然而,事实并非如此。相反,我可以在调试期间看到,尽管 Challenge() 被点击 - 页面仍在继续加载。在我的 Claims.aspx 页面上 - 请求立即返回到 keycloak 并返回 302。

这让我发现所有其他页面(抱歉,我在这里有点迷失)都源自

AuthPage
而不仅仅是
System.Web.UI.Page
。作为一名新手开发人员,这是我第一次在实现此功能时看到请求命中此 AuthPage 类(以及 Global.asax.cs 的 Application_AuthenticateRequest 方法,但也许我之前没有注意到)。

AuthPage.cs

private void CheckSession(object sender, EventArgs ea)
{
    /*OpenID authentication */
    if (!Request.IsAuthenticated)
    {
        HttpContext.Current.GetOwinContext().Authentication.Challenge(new AuthenticationProperties { RedirectUri = "~/test.aspx", AllowRefresh = true, IsPersistent = true });
    }

    /* Forms authentication */ /*
    object o = Session["Admin"];

    if ((o is Administrator) == false)
    {
        // OK, not logged in, save the address of this page 
        // so we can return to it if/when the admin logs in
        Session["GoToWhenLoggedIn"] = Request.Url.PathAndQuery;
        Response.Redirect(Config.LoginPageName);
    } */

}

在这里我找到了这个神奇的 CheckSession,所有“我需要进行这项工作的其他页面”都继承并注释掉了以前的表单身份验证方法。

在它的位置我再次尝试放入

HttpContext.Current.GetOwinContext().Authentication.Challenge(new AuthenticationProperties { RedirectUri = "~/test.aspx", AllowRefresh = true, IsPersistent = true });

但是结果是一样的。一旦这条线运行,

HttpContext
包含它返回的响应,并且实际上在检查它时没有产生我可以看到的“重定向URL”。显然,在旧的实现中,是
Response.Redirect....
执行此操作。

我希望 OWin 在这些页面上的表现就像在我的 Claims.aspx 上一样。

我注意到

Challenge
方法的摘要如下:

Summary:
Add information into the response environment that will cause 
the authentication middleware to challenge the caller to authenticate. 
This also changes the status code of the response to 401. 
The nature of that challenge varies greatly, and 
ranges from adding a response header or 
changing the 401 status code to a 302 redirect.

显然我不能再继承AuthPage,但我想尽可能少地接触“现有应用程序”,以免破坏其他任何东西。将不胜感激任何建议。谢谢大家。

编辑:

忘记包含此代码片段。 Startup.Cs 中的

app.UseExternalSignInCookie
实际上就是这个类,其 AuthentiationMode 显式设置为 Active:

public static void UseExternalSignInCookie(this IAppBuilder app)
{
    UseExternalSignInCookie(app, DefaultAuthenticationTypes.ExternalCookie);
}

public static void UseExternalSignInCookie(this IAppBuilder app, string externalAuthenticationType)
{
    if (app == null)
    {
        throw new ArgumentNullException("app");
    }

    app.SetDefaultSignInAsAuthenticationType(externalAuthenticationType);
    app.UseCookieAuthentication(new CookieAuthenticationOptions
    {
        AuthenticationType = externalAuthenticationType,
        AuthenticationMode = AuthenticationMode.Active,
        CookieName = CookiePrefix + externalAuthenticationType,
        ExpireTimeSpan = TimeSpan.FromMinutes(5),
    });
}
asp.net-identity openid-connect owin .net-4.8 owin-middleware
1个回答
0
投票

所以我深入了解了这件事,是的,这很尴尬。本质上,还没有设置一个干净的演示页面,然后将其移植到“真实”页面 - 我犯了一个心理错误,假设当挑战()被击中时,所有线程都被中断并重定向到陆地你去。由于干净的演示页面上没有其他页面,我可以看到我是如何到达那里的。实际发生的情况是,即使在页面顶部点击 Challenge(),它也只是设置响应的中间件 - 页面的其余部分仍然需要完成运行到代码结束,即使它没有发送到或在客户端 DOM 中绘制。

鉴于每个初级人员都接触过旧的 .NET Framework 遗留代码,它有很多元素会返回 null,并且当没有 Session(它们自行处理而不是 DbContext)来提取数据时,所有这些元素肯定都会返回 NullExceptions从。所以我遇到了异常并假设我破坏了某些东西。

我通过捕获仅来自所有页面继承的 MasterMain 上的 System.Web 的 NullExceptions 解决了这个问题。丑陋,我知道——但是有数百页,将它们全部修复起来将是一件巨大的痛苦。此外,当我在那里时,我将会话检查移至“CheckSession”,因为我不再需要 login.aspx。

© www.soinside.com 2019 - 2024. All rights reserved.