Как добавить OpenIdConnect через IdentityServer4 в ASP. NET Core ServerSide Blazor веб-приложение? - PullRequest
2 голосов
/ 08 января 2020

Я сделал следующее (должно работать, но не работает), без перенаправления, без ошибок, без ничего, просто отображается страница без аутентификации, что я делаю не так?


ASP. NET Core 3.1 Blazor

Шаг 1. Install-Package 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 for development only
           options.ClientId = "mywebclient";
           options.ResponseType = "code";
           options.UsePkce = true;
           options.Scope.Add("profile");
           options.Scope.Add("offline_access");
           options.SaveTokens = true;
        });

В «Configure» добавить

        ...
        services.AddAuthorization();

        app.UseStaticFiles();

        app.UseRouting();


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

        ....

Шаг 3. Добавить атрибут Авторизоваться на странице блейзора

        @page "/item"
        @attribute [Authorize] 

Ответы [ 3 ]

5 голосов
/ 10 января 2020

Ваш код страдает от нескольких недугов ... Основная проблема заключается в том, что в вашем коде отсутствует механизм запроса вызова проверки подлинности, который обеспечивает перенаправление на агент проверки подлинности, такой как IdentityServer. Это возможно только с HttpContext, который недоступен в SignalR (Blazor Server App). Чтобы решить эту проблему, мы добавим пару страниц Razor, где доступен HttpContext. Подробнее в ответ ...

Ниже приведено полное и рабочее решение вопроса:

  1. Создание приложения Blazor Server.
  2. Install-Package Microsoft .AspNetCore.Authentication.OpenIdConnect -Version 3.1.0
  3. Создать компонент с именем LoginDisplay (LoginDisplay.razor) и поместить его в общую папку. Этот компонент используется в компоненте MainLayout

    <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>

Примечание. Чтобы перенаправить запросы на вход и выход на IdentityServer, нам нужно создать две страницы Razor следующим образом: 1. Создать страницу Razor для входа Login.cs html (Login.cs * 1084). * .cs) и поместите их в папку «Страницы» следующим образом:

Login.cs html .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 } );
    }  
}

Этот код запускает вызов для схемы аутентификации Open Id Connect, которую вы определено в классе запуска.

Создайте страницу Logaz Razor Logout.cs html (Logout.cs html .cs) и поместите их также в папку Pages:

Logout.cs html. cs

using Microsoft.AspNetCore.Authentication;

public class LogoutModel : PageModel { public async Task<IActionResult> OnGetAsync() { await HttpContext.SignOutAsync(); return Redirect("/"); } }

Этот код выводит вас, перенаправляя на домашнюю страницу вашего приложения Blazor.

Замените код в App.razor следующим кодом:

@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>

Замените код в классе запуска следующим:

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

ВАЖНО : во всем приведенном выше примере кода вам придется добавлять операторы по мере необходимости. Большинство из них предоставляются по умолчанию. Предоставленное здесь использование - это то, что необходимо для включения процесса аутентификации и авторизации.

  • Запустите ваше приложение, нажмите на кнопку входа в систему для аутентификации. Вы будете перенаправлены на тестовый сервер IdentityServer, который позволит вам выполнить вход с OID C. Вы можете ввести имя пользователя: bob и пароль bob , и после нажатия кнопки ОК вы будете перенаправлены на домашнюю страницу. Также обратите внимание, что вы можете использовать внешний логин провайдера Google (попробуйте). Обратите внимание, что после входа в систему с помощью сервера идентификации компонент LoginDisplay отображает строку «Hello».

Примечание. Во время экспериментов с приложением следует очистить данные просмотра, если вы хотите быть перенаправлены на страницу входа на сервер идентификации, в противном случае ваш браузер может использовать кэшированные данные. Помните, что это механизм авторизации на основе Cook ie ...

Обратите внимание, что создание механизма входа в систему, как это делается здесь, не делает ваше приложение более защищенным, чем раньше. Любой пользователь может получить доступ к вашим веб-ресурсам без необходимости входа в систему вообще. Чтобы защитить части вашего веб-сайта, вы также должны реализовать авторизацию, обычно аутентифицированный пользователь авторизуется для доступа к защищенному ресурсу, если не применяются другие меры, такие как роли, политики и т. Д. c. Ниже приведена демонстрация того, как вы можете защитить свою страницу Fetchdata от неавторизованных пользователей (опять же, аутентифицированный пользователь считается авторизованным для доступа к странице Fetchdata).

  1. В верхней части страницы компонента Fetchdata добавьте @ Директива атрибута для атрибута Authorize, например: @attribute [Authorize] Когда пользователь, не прошедший проверку подлинности, пытается получить доступ к странице Fetchdata, выполняется свойство делегата AuthorizeRouteView.NotAuthorized, поэтому мы можем добавить некоторый код для перенаправления пользователя на страницу входа того же сервера идентификации аутентифицировать.
  2. Код в элементе NotAuthorized выглядит следующим образом:

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

Получает URL последней страницы, к которой вы пытались получить доступ, страницу Fetchdata, а затем переходит на страницу Login Razor, с которой выполняется запрос пароля, то есть пользователь перенаправляется на страницу входа на сервер идентификации.

После аутентификации пользователь перенаправляется на страницу Fetchdata.

Удачи ...

0 голосов
/ 08 января 2020

Официальной поддержки IdentityServer и OID C flow в Blazor пока нет. Я уже открыл несколько выпусков на github aspnetcore, но они всегда закрывались без правильного ответа.

Лучшие источники, которые я нашел для В данный момент ведутся следующие блоги:

https://mcguirev10.com/2019/12/15/blazor-authentication-with-openid-connect.html

https://wellsb.com/csharp/aspnet/blazor-httpclientfactory-and-web-api/

0 голосов
/ 08 января 2020

он просто отображает страницу без авторизации, что я делаю не так

Вероятно, вы не добавили AuthorizeRouteView для своего приложения. Обратите внимание, что @attribute [Authorize] является только директивой, которая добавляет [AuthorizeAttribute] для этого компонента страницы. Чтобы включить авторизацию для маршрута, вам необходимо:

  1. Добавить <CascadingAuthenticationState> на самом верхнем уровне
  2. Добавить <AuthorizeRouteView>, чтобы включить авторизацию для маршрута. См. официальные документы
  3. Используйте <AuthorizeView> для простой авторизации компонента. Добавьте <NotAuthorized> для отображения компонента, когда он не авторизован. И используйте Authorizing для отображения компонента при авторизации пользователя. См. официальные документы

Например, вы BlazorApp.razor могли бы выглядеть так:

<CascadingAuthenticationState>
    <Router AppAssembly="@typeof(Program).Assembly">
        <Found Context="routeData">
            <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" >
                <NotAuthorized>
                    You're not allowed!
                </NotAuthorized>
                <Authorizing>
                    <h1>Authentication in progress</h1>
                </Authorizing>
            </AuthorizeRouteView>
        </Found>
        <NotFound>
            <LayoutView Layout="@typeof(MainLayout)">
                <p>Sorry, there's nothing at this address.</p>
            </LayoutView>
        </NotFound>
    </Router>
</CascadingAuthenticationState>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...