如何通过 IdentityServer4 将 OpenIdConnect 添加到 ASP.NET Core ServerSide Blazor Web 应用程序? [英] How to add OpenIdConnect via IdentityServer4 to ASP.NET Core ServerSide Blazor web app?

查看:25
本文介绍了如何通过 IdentityServer4 将 OpenIdConnect 添加到 ASP.NET Core ServerSide Blazor Web 应用程序?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我做了以下事情(它应该可以工作,但没有),没有重定向,没有错误,什么也没有,它只是显示没有身份验证的页面,我做错了什么?

<小时>

ASP.NET Core 3.1 Blazor

步骤 1. 安装包 Microsoft.AspNetCore.Authentication.OpenIdConnect

第 2 步.编辑 Statup.cs

在ConfigurationServices"下添加

 services.AddAuthentication(options =>{options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;}).AddCookie().AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options =>{options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;options.Authority = "http://localhost:5000";options.RequireHttpsMetadata = false;//false 仅用于开发options.ClientId = "mywebclient";options.ResponseType = "代码";options.UsePkce = true;options.Scope.Add("profile");options.Scope.Add("offline_access");options.SaveTokens = true;});

在配置"下添加

<代码> ...services.AddAuthorization();app.UseStaticFiles();app.UseRouting();app.UseAuthentication();app.UseAuthorization();....

步骤 3. 向 blazor 页面添加授权属性

 @page "/item"@attribute [授权]

解决方案

您的代码存在一些问题...主要问题是您的代码没有提供身份验证质询请求机制,无法重定向到身份验证代理,例如身份服务器.这仅适用于 HttpContext,它在 SignalR(Blazor 服务器应用程序)中不可用.为了解决这个问题,我们将添加几个 Razor 页面,其中 HttpContext 可用.更多答案...

以下是该问题的完整且有效的解决方案:

  1. 创建 Blazor 服务器应用.
  2. 安装包 Microsoft.AspNetCore.Authentication.OpenIdConnect -Version 3.1.0
  3. 创建一个名为 LoginDisplay (LoginDisplay.razor) 的组件,并将其放置在 Shared 文件夹中.该组件在MainLayout组件中使用

    <授权><a href="logout">你好,@context.User.Identity.Name !</a><form method="get" action="logout"><button type="submit" class="nav-link btn btn-link">注销</button></表单></授权><未授权><a href="login?redirectUri=/">登录</a></NotAuthorized></AuthorizeView>

    在MainLayout组件中添加LoginDisplay组件,就在About上面锚元素,像这样<代码><div class="top-row px-4"><登录显示/><a href="https://docs.microsoft.com/aspnet/" target="_blank">关于</a>

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

登录.cshtml.cs

使用 Microsoft.AspNetCore.Authentication;使用 Microsoft.AspNetCore.Authentication.OpenIdConnect;使用 Microsoft.AspNetCore.Authentication.Cookies;使用 Microsoft.IdentityModel.Tokens;公共类 LoginModel : PageModel{公共异步任务 OnGet(string redirectUri){等待 HttpContext.ChallengeAsync("oidc", newAuthenticationProperties { RedirectUri = redirectUri } );}}

此代码开始对您在 Startup 类中定义的 Open Id Connect 身份验证方案进行质询.

  1. 创建一个注销 Razor 页面 Logout.cshtml (Logout.cshtml.cs) 并将它们也放在 Pages 文件夹中:

    Logout.cshtml.cs

    使用 Microsoft.AspNetCore.Authentication;

    公共类 LogoutModel : PageModel{公共异步任务OnGetAsync(){等待 HttpContext.SignOutAsync();返回重定向(/");}}

此代码将您注销,将您重定向到 Blazor 应用的主页.

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

@inject NavigationManager NavigationManager<CascadingAuthenticationState><Router AppAssembly="@typeof(Program).Assembly"><Found Context="routeData"><AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)"><未授权>@{var returnUrl = NavigationManager.ToBaseRelativePath(NavigationManager.Uri);NavigationManager.NavigateTo($"login?redirectUri={returnUrl}", forceLoad: true);}</NotAuthorized><授权>等待...</授权></AuthorizeRouteView></发现><未找到><LayoutView Layout="@typeof(MainLayout)"><p>抱歉,此地址没有任何内容.</p></LayoutView></NotFound></路由器></CascadingAuthenticationState>

将 Startup 类中的代码替换为以下内容:

使用 Microsoft.AspNetCore.Authentication.OpenIdConnect;使用 Microsoft.AspNetCore.Authorization;使用 Microsoft.AspNetCore.Mvc.Authorization;使用 System.Net.Http;使用 Microsoft.AspNetCore.Authentication.Cookies;使用 Microsoft.IdentityModel.Tokens;使用 Microsoft.IdentityModel.Protocols.OpenIdConnect;使用 Microsoft.IdentityModel.Logging;公开课启动{公共启动(IConfiguration配置){配置=配置;}公共 IConfiguration 配置 { 获取;}//这个方法被运行时调用.使用此方法向容器添加服务.//有关如何配置应用程序的详细信息,请访问 https://go.microsoft.com/fwlink/?LinkID=398940public void ConfigureServices(IServiceCollection 服务){services.AddRazorPages();services.AddServerSideBlazor();services.AddAuthorizationCore();services.AddSingleton();services.AddAuthentication(sharedOptions =>{sharedOptions.DefaultAuthenticateScheme =CookieAuthenticationDefaults.AuthenticationScheme;sharedOptions.DefaultSignInScheme =CookieAuthenticationDefaults.AuthenticationScheme;sharedOptions.DefaultChallengeScheme =OpenIdConnectDefaults.AuthenticationScheme;}).AddCookie().AddOpenIdConnect("oidc", options =>{options.Authority = "https://demo.identityserver.io/";options.ClientId = "interactive.confidential.short";options.ClientSecret = "秘密";options.ResponseType = "代码";options.SaveTokens = true;options.GetClaimsFromUserInfoEndpoint = true;options.UseTokenLifetime = false;options.Scope.Add("openid");options.Scope.Add("profile");options.TokenValidationParameters = 新令牌验证参数{NameClaimType = "姓名"};options.Events = 新的 OpenIdConnectEvents{OnAccessDenied = 上下文 =>{context.HandleResponse();context.Response.Redirect("/");返回 Task.CompletedTask;}};});}//这个方法被运行时调用.使用此方法进行配置HTTP 请求管道.公共无效配置(IApplicationBuilder 应用程序,IWebHostEnvironment 环境){如果 (env.IsDevelopment()){app.UseDeveloperExceptionPage();}别的{app.UseExceptionHandler("/Error");//默认的 HSTS 值为 30 天.您可能希望针对生产场景更改此设置,请参阅 https://aka.ms/aspnetcore-hsts.app.UseHsts();}app.UseHttpsRedirection();app.UseStaticFiles();app.UseRouting();app.UseAuthentication();app.UseAuthorization();app.UseEndpoints(endpoints =>{端点.MapBlazorHub();endpoints.MapFallbackToPage("/_Host");});}}

重要事项:在上面的所有代码示例中,您必须根据需要添加 using 语句.其中大多数是默认提供的.此处提供的使用是启用身份验证和授权流程所必需的.

  • 运行您的应用,点击登录按钮进行身份验证.您将被重定向到 IdentityServer 测试服务器,该服务器允许您执行 OIDC 登录.您可以输入用户名:bob 和密码bob,点击确定按钮后,您将被重定向到您的主页.另请注意,您可以使用外部登录提供商 Google(试试看).请注意,使用身份服务器登录后,LoginDisplay 组件将显示字符串Hello,".

注意:当你在试验你的应用程序时,你应该清除浏览数据,如果你想被重定向到身份服务器的登录页面,否则你的浏览器可能会使用缓存的数据.请记住,这是一种基于 cookie 的授权机制...

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

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

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

这将检索您尝试访问的最后一个页面的 url,即 Fetchdata 页面,然后导航到执行密码质询的 Login Razor 页面,即,用户被重定向到身份服务器的登录页面进行身份验证.

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

祝你好运...

I did the following (It should work but it does not), no redirect, no error, no nothing, it just displays the page without auth, what am I doing wrong?


ASP.NET Core 3.1 Blazor

Step 1. Install-Package Microsoft.AspNetCore.Authentication.OpenIdConnect

Step 2. Edit Statup.cs

Under "ConfigurationServices" add

        services.AddAuthentication(options =>
        {
            options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
        })
        .AddCookie()
        .AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options =>
        {
           options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
           options.Authority = "http://localhost:5000";
           options.RequireHttpsMetadata = false; //false for development only
           options.ClientId = "mywebclient";
           options.ResponseType = "code";
           options.UsePkce = true;
           options.Scope.Add("profile");
           options.Scope.Add("offline_access");
           options.SaveTokens = true;
        });

Under "Configure" add

        ...
        services.AddAuthorization();

        app.UseStaticFiles();

        app.UseRouting();


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

        ....

Step 3. Add attribute Authorize to blazor page

        @page "/item"
        @attribute [Authorize] 

解决方案

Your code suffers from a couple of maladies... The main issue is that your code provides no authentication challenge request mechanism that enables redirection to an authenticating agent such as IdentityServer. This is only possible with HttpContext, which is not available in SignalR (Blazor Server App). To solve this issue we'll add a couple of Razor pages where the HttpContext is available. More in the answer...

The following is a complete and working solution to the question:

  1. Create a Blazor Server App.
  2. Install-Package Microsoft.AspNetCore.Authentication.OpenIdConnect -Version 3.1.0
  3. 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>

    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>

Note: In order to redirect requests for login and for logout to IdentityServer, 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:

Login.cshtml.cs

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("oidc", new 
            AuthenticationProperties { RedirectUri = redirectUri } );
    }  
}

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

  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("/"); } }

This code signs you out, redirecting you to the Home page of your Blazor app.

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

@inject NavigationManager NavigationManager

<CascadingAuthenticationState>
<Router AppAssembly="@typeof(Program).Assembly">
    <Found Context="routeData">
        <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
            <NotAuthorized>
                @{
                    var returnUrl = NavigationManager.ToBaseRelativePath(NavigationManager.Uri);

                    NavigationManager.NavigateTo($"login?redirectUri={returnUrl}", forceLoad: true);

                }

            </NotAuthorized>
            <Authorizing>
                Wait...
            </Authorizing>
        </AuthorizeRouteView>
    </Found>
    <NotFound>

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

    </NotFound>

</Router>
</CascadingAuthenticationState>

Replace the code in the Startup class with the following:

using Microsoft.AspNetCore.Authentication.OpenIdConnect; 
using Microsoft.AspNetCore.Authorization; 
using Microsoft.AspNetCore.Mvc.Authorization; 
using System.Net.Http;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.IdentityModel.Tokens;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.IdentityModel.Logging;


public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddRazorPages();
        services.AddServerSideBlazor();
        services.AddAuthorizationCore();
        services.AddSingleton<WeatherForecastService>();

        services.AddAuthentication(sharedOptions =>
        {
            sharedOptions.DefaultAuthenticateScheme = 
                 CookieAuthenticationDefaults.AuthenticationScheme;
            sharedOptions.DefaultSignInScheme = 
                CookieAuthenticationDefaults.AuthenticationScheme;
            sharedOptions.DefaultChallengeScheme = 
               OpenIdConnectDefaults.AuthenticationScheme;
        })
        .AddCookie()
        .AddOpenIdConnect("oidc", options =>
         {
             options.Authority = "https://demo.identityserver.io/";
             options.ClientId = "interactive.confidential.short"; 
             options.ClientSecret = "secret";
             options.ResponseType = "code";
             options.SaveTokens = true;
             options.GetClaimsFromUserInfoEndpoint = true;
             options.UseTokenLifetime = false;
             options.Scope.Add("openid");
             options.Scope.Add("profile");
             options.TokenValidationParameters = new 
                    TokenValidationParameters
                    {
                        NameClaimType = "name"
                    };

             options.Events = new OpenIdConnectEvents
             {
               OnAccessDenied = context =>
                        {
                          context.HandleResponse();
                          context.Response.Redirect("/");
                          return Task.CompletedTask;
                       }
       };
 });

}


  // 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");
        });
    }
}

IMPORTANT: in all the code sample above you'll have to add using statements as necessary. Most of them are provided by default. The using provided here are those necessary to enable the authentication and authorization flow.

  • Run your app, click on the log in button to authenticate. You are being redirected to IdentityServer test server which allows you to perform an OIDC login. You may enter a user name: bob and password bob, and after click the OK button, you'll be redirected to your home page. Note also that you can use the external login provider Google (try it). Note that after you've logged in with identity server, the LoginDisplay component displays the string "Hello, ".

Note: While you're experimenting with your app, you should clear the browsing data, if you want to be redirected to the identity server's login page, otherwise, your browser may use the cached data. Remember, this is a cookie-based authorization mechanism...

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 secure your Fetchdata page from unauthorized users (again, authenticated user is considered authorized to access the Fetchdata page).

  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 identity server's login page to authenticate.
  2. The code within the NotAuthorized element looks like this:

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

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

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

Good luck...

这篇关于如何通过 IdentityServer4 将 OpenIdConnect 添加到 ASP.NET Core ServerSide Blazor Web 应用程序?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆