Blazor(服务器端)和Okta

问题描述:

我目前正在阅读这篇文章,以将okta集成到Blazor服务器端应用程序中

I am currently going through this post to integrate okta into a Blazor Server Side App

https://developer.okta.com/blog /2019/10/16/csharp-blazor-authentication

我目前收到的消息是抱歉,此地址没有任何内容".

I am currently getting "Sorry, there's nothing at this address".

我希望有人可以为我的问题提供建议.

I was hoping someone could provide a suggestion to my problem.

或者没有人知道将okta集成到Blazor Server Side App中的示例吗?

请让我知道.

任何帮助将不胜感激.我完全在转动我的车轮.

Any help would be appreciated. I am totally spinning my wheels.

这是我的okta常规设置:

下面是我的解决方案中的代码(我多次审查该帖子以确保它是正确的.但是我想我可能会漏掉一些东西.)

Below is code from my solution (I reviewed the post so many times to make sure it is right. But I guess I could of missed something.)

启动ConfigureServices

       services.AddAuthorizationCore();
        services.AddAuthentication(sharedOptions =>
            {
                sharedOptions.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                sharedOptions.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                sharedOptions.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
            })
            .AddCookie()
            .AddOpenIdConnect(options =>
            {
                options.ClientId = Configuration["Okta:ClientId"];
                options.ClientSecret = Configuration["Okta:ClientSecret"];
                options.Authority = Configuration["Okta:Issuer"];
                options.CallbackPath = "/authorization-code/callback";
                options.ResponseType = "code";
                options.SaveTokens = true;
                options.UseTokenLifetime = false;
                options.GetClaimsFromUserInfoEndpoint = true;
                options.Scope.Add("openid");
                options.Scope.Add("profile");
                options.TokenValidationParameters = new TokenValidationParameters
                {
                    NameClaimType = "name"
                };
            });
    }

启动配置

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Error");
            // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
            app.UseHsts();
        }

        app.UseHttpsRedirection();

        app.UseStaticFiles();

        app.UseRouting();

        app.UseAuthentication();
        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapBlazorHub();
            endpoints.MapFallbackToPage("/_Host");
        });
    }

MainLayout.razor

<div class="top-row px-4">

    <AuthorizeView>
        <Authorized>
            <a href="Identity/Account/Manage">Hello, @context.User.Identity.Name!</a>
            <a href="Identity/Account/LogOut">Log out</a>
        </Authorized>
        <NotAuthorized>
            <a href="Identity/Account/Register">Register</a>
            <a href="Identity/Account/Login">Log in</a>
        </NotAuthorized>
    </AuthorizeView>

    <a href="https://docs.microsoft.com/en-us/aspnet/" target="_blank">About</a>
</div>

App.razor

<CascadingAuthenticationState>
    <Router AppAssembly="@typeof(Program).Assembly">
        <Found Context="routeData">
            <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
        </Found>
        <NotFound>
            <LayoutView Layout="@typeof(MainLayout)">
                <p>Sorry, there's nothing at this address.</p>
            </LayoutView>
        </NotFound>
    </Router>
</CascadingAuthenticationState> ```



单击登录"锚元素时,路由器无法找到身份/帐户/登录"路径,其结果是显示

对不起,此地址没有任何内容.

When you click on the "Log in" anchor element, the "Identity/Account/Login" path cannot be found by the Router, the result of which is displaying

Sorry, there's nothing at this address.

我不明白她为什么如此设置AuthorizeView组件.不过,在我看来,她的整个文章是由市政委员会设计的,并由备用代码段汇编而成.但是,这超出了此答案的范围.

It is beyond me to understand why she set up the AuthorizeView component like this. It seems to me, though, that her whole article was designed by a municipal committee, and assembled from spare code snippets. However, this is beyond the scope of this answer.

  1. 创建一个名为LoginDisplay(LoginDisplay.razor)的组件,并将其放置在Shared文件夹中.该组件用于MainLayout组件

  1. Create a component named LoginDisplay (LoginDisplay.razor), and place it in the Shared folder. This component is used in the MainLayout component

<AuthorizeView> <Authorized> <a href="logout">Hello, @context.User.Identity.Name!</a> <form method="get" action="logout"> <button type="submit" class="nav-link btn btn-link">Log out</button> </form> </Authorized> <NotAuthorized> <a href="login?redirectUri=/">Log in</a> </NotAuthorized> </AuthorizeView>

<AuthorizeView> <Authorized> <a href="logout">Hello, @context.User.Identity.Name!</a> <form method="get" action="logout"> <button type="submit" class="nav-link btn btn-link">Log out</button> </form> </Authorized> <NotAuthorized> <a href="login?redirectUri=/">Log in</a> </NotAuthorized> </AuthorizeView>

将LoginDisplay组件添加到MainLayout组件中,位于About的正上方 锚元素,像这样 <div class="top-row px-4"> <LoginDisplay /> <a href="https://docs.microsoft.com/aspnet/" target="_blank">About</a> </div>

Add the LoginDisplay component to the MainLayout component, just above the About anchor element, like this <div class="top-row px-4"> <LoginDisplay /> <a href="https://docs.microsoft.com/aspnet/" target="_blank">About</a> </div>

注意:为了将登录和注销请求重定向到okta,我们必须创建两个Razor页面,如下所示: 1.创建一个登录剃刀"页面Login.cshtml(Login.cshtml.cs),并将其放置在Pages文件夹中,如下所示:

Note: In order to redirect requests for login and for logout to okta, we have to create two Razor pages as follows: 1. Create a Login Razor page Login.cshtml (Login.cshtml.cs) and place them in the Pages folder as follow:

 using Microsoft.AspNetCore.Authentication;
 using Microsoft.AspNetCore.Authentication.OpenIdConnect;
 using Microsoft.AspNetCore.Authentication.Cookies;
 using Microsoft.IdentityModel.Tokens;

public class LoginModel : PageModel
{
    public async Task OnGet(string redirectUri)
    {
        await 
    HttpContext.ChallengeAsync(OpenIdConnectDefaults.AuthenticationScheme, 
    new AuthenticationProperties
    {
            RedirectUri = redirectUri
    });
  }
}

此代码对您在Startup类中定义的Open Id Connect身份验证方案提出了挑战.

This code starts the challenge for the Open Id Connect authentication scheme you defined in the Startup class.

  1. 创建注销剃刀"页面Logout.cshtml(Logout.cshtml.cs)并将它们也放置在Pages文件夹中:

    Logout.cshtml.cs

  1. Create a Logout Razor page Logout.cshtml (Logout.cshtml.cs) and place them in the Pages folder as well:

    Logout.cshtml.cs

using Microsoft.AspNetCore.Authentication; public class LogoutModel : PageModel { public async Task<IActionResult> OnGetAsync() { await HttpContext.SignOutAsync(); return Redirect("/"); } } 此代码会将您注销,从而将您重定向到Blazor应用程序的主页.

using Microsoft.AspNetCore.Authentication; public class LogoutModel : PageModel { public async Task<IActionResult> OnGetAsync() { await HttpContext.SignOutAsync(); return Redirect("/"); } } This code sign you out, redirecting you to the Home page of your Blazor app.

用以下代码替换App.razor中的代码:

Replace the code in App.razor with the following code:

<CascadingAuthenticationState>

<Router AppAssembly="@typeof(Program).Assembly">
    <Found Context="routeData">
        <AuthorizeRouteView RouteData="@routeData" 
      DefaultLayout="@typeof(MainLayout)">
            <NotAuthorized>

                  @*<RedirectToLogin />*@
            </NotAuthorized>
            <Authorizing>
                Wait...
            </Authorizing>
        </AuthorizeRouteView>
    </Found>
    <NotFound>

            <LayoutView Layout="@typeof(MainLayout)">
                <p>Sorry, there's nothing at this address.</p>
            </LayoutView>

    </NotFound>

 </Router>
 </CascadingAuthenticationState>

运行您的应用,单击登录"按钮进行身份验证

Run your app, click on the log in button to authenticate

注意:我还没有完成我的回答...还有很多要补充的内容,但现在不能做.我会尽快做的.如果您有任何疑问,请随时提出.如果可以的话,我会回答他们;;)

Note: I've not finished my answer...I've more to add, but I can't do it now. I'll do it as soon as I can. If you have questions, please don't hesitate to ask. I'll answer them, if I can...;)

注意:此示例非常适合我.

Note: This sample works for me great.

请不要忘记您的appsettings.json文件.应该是这样的:

Please don't forget your appsettings.json file. It should be something like this:

{
  "Okta": {
    "Issuer": "https://dev-621531.okta.com/oauth2/default",
    "ClientId": "0o1a5bsivg6wFDw6Jr347",
    "ClientSecret": "ffolkG3sd2NgQ_E909etXRU3cXX3wBpgE0XxcmF5"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

请勿更改设置,因为我已经更改了... Startup类中的代码是正确的,但是您可能需要添加一些名称空间引用,也许是我添加的nuget包等.

Don't try to use my settings, as I've changed them... The code in the Startup class is correct, but you may need to add some namespace references, perhaps nuget packages which I added,etc.

注意:在尝试应用程序时,如果要重定向到okta的登录页面,则应清除浏览数据,否则,浏览器可能会使用缓存的数据.

Note: While you're experimenting with your app, you should clear the browsing data, if you want to be redirected to okta's login page, otherwise, your browser may use the cached data.

更新 请注意,按照此处的步骤创建登录机制不会使您的应用程序比以前更安全.任何用户都无需登录即可访问您的Web资源.为了保护网站的某些部分,您还必须实施授权,通常,经过身份验证的用户有权访问受保护的资源,除非实施了其他措施,例如角色,策略等.以下是演示如何进行的.您可以防止未经授权的用户保护Fetchdata页面(再次,经过身份验证的用户被视为有权访问Fetchdata页面).

Update Note that creating a login mechanism as is done here does not make your app more secured than before. Any user can access your web resources without needing to log in at all. In order to secure parts of your web site, you have to implement authorization as well, conventionally, an authenticated user is authorized to access secured resource, unless other measures are implemented, such as roles, policies, etc. The following is a demonstration how you can secured your Fetchdata page from unauthorized users (again, authenticated user is considered authorized to access the Fetchdata page).

  1. 在Fetchdata组件页面的顶部,为Authorize属性添加@attribute指令,如下所示:@attribute [Authorize] 当未经身份验证的用户尝试访问Fetchdata页面时,将执行AuthorizeRouteView.NotAuthorized委托属性,因此我们可以添加一些代码以将用户重定向到同一okta的登录页面以进行身份​​验证.
  2. 将此代码放置在NotAuthorized元素内,如下所示:

  1. At the top of the Fetchdata component page add the @attribute directive for the Authorize attribute, like this: @attribute [Authorize] When an unauthenticated user tries to access the Fetchdata page, the AuthorizeRouteView.NotAuthorized delegate property is executed, so we can add some code to redirect the user to the same okta's login page to authenticate.
  2. place this code within the NotAuthorized element, like this:

<NotAuthorized> @{ var returnUrl = NavigationManager.ToBaseRelativePath(NavigationManager.Uri); NavigationManager.NavigateTo($"login?redirectUri= {returnUrl}", forceLoad: true); } </NotAuthorized>

<NotAuthorized> @{ var returnUrl = NavigationManager.ToBaseRelativePath(NavigationManager.Uri); NavigationManager.NavigateTo($"login?redirectUri= {returnUrl}", forceLoad: true); } </NotAuthorized>

这将检索您尝试访问的最后一个页面的Fetchdata页面的URL,然后导航到执行密码挑战的Login Razor页面,即将用户重定向到okta的登录页面进行身份验证.

This retrieves the url of the last page you were trying to access, the Fetchdata page, and then navigate to the Login Razor page from which a password challenge is performed, that is the user is redirected to okta's login page to authenticate.

用户身份验证后,他将被重定向到Fetchdata页面.

After the user has authenticated he's redirected to the Fetchdata page.

最后但并非最不重要的一点是,在Fetchdata页面的顶部添加以下内容: @page "/authorization-code/fetchdata"

Last but not least, add the following at the top of the Fetchdata page: @page "/authorization-code/fetchdata"

请注意,Fetchdata组件已经有一个@page指令,这是第二个.在Blazor中,多个路由模板是合法的...但是为什么要添加它呢?这是因为从okta传递到我们的应用的返回URL是"/authorization-code/fetchdata",而不是我们期望的"/fetchdata",因为我们从导航管理器中提取的returnUrl是"fetchdata".我不确定我是否了解"/authorization-code/"的角色,以及为什么对我们强制执行它.我真的没有时间去调查.但是,okta需要此url段,无论在我的代码中还是在okta网站的仪表板中,任何尝试不使用它的尝试都失败了.我可以在代码中摆脱这个url段,但这不是问题.我只是想知道为什么我们需要提供它,所以我可以更好地处理它.如果您碰巧了解了更多信息,请在此处报告,因为我无法花更多时间进行调查...

Note that the Fetchdata component has already have a @page directive, and this one is the second. Multiple route templates are legitimate in Blazor... But why do I add it ? This is because the return url passed from okta to our app is "/authorization-code/fetchdata", and not "/fetchdata", as we might have expected, as the returnUrl we extracted from the navigation manager is "fetchdata". I'm not sure I understand the role of "/authorization-code/", and why it is enforced on us. I really did not have the time to investigate. However, this url segment is required by okta, and any attempt to do without it has failed, both in my code and in the dashboard in okta web site. I can get rid of this url segment in code, but this is not an issue. I just want to know why we need to provide it, so I can deal with it better. If you happen to learn more about it, please report it here, as I cannot spend more time investigating...

顺便说一句,本文中提供的解决方案并不完整.我敢说,相当偏颇.

Incidentally, the solution provided in the article is by no mean complete; I dare say, rather partial.

祝你好运...