服务器端Blazor中的OIDC身份验证 [英] OIDC authentication in server-side Blazor

查看:105
本文介绍了服务器端Blazor中的OIDC身份验证的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用了这种方法,但由于某种原因它是不正确的,因为 @attribute [AllowAnonymous] 并没有真正起作用,所以我使用了 [Authorized] 属性而不是 [AllowAnonymous] ,然后删除 RequireAuthenticatedUser ,但OIDC不会将客户端重定向到服务器登录页面.

I used this method but somehow it's not right because @attribute [AllowAnonymous] doesn't really worked so I use [Authorized] attribute instead of [AllowAnonymous] and then remove RequireAuthenticatedUser but OIDC does not redirect client to server login page.

我检查了 SteveSanderson github文章关于blazor中的身份验证和授权,但他没有发言关于OIDC.

I checked SteveSanderson github article about authentication and authorization in blazor but he didn't talk about OIDC.

那我该如何处理呢?

启动类:

services.AddAuthentication(config =>
{
    config.DefaultScheme = "Cookie";
    config.DefaultChallengeScheme = "oidc";
})
    .AddCookie("Cookie")
    .AddOpenIdConnect("oidc", config =>
    {
        config.Authority = "https://localhost:44313/";
        config.ClientId = "client";
        config.ClientSecret = "secret";
        config.SaveTokens = true;
        config.ResponseType = "code";
        config.SignedOutCallbackPath = "/";
        config.Scope.Add("openid");
        config.Scope.Add("api1");
        config.Scope.Add("offline_access");
    });

services.AddMvcCore(options =>
{
    var policy = new AuthorizationPolicyBuilder()
        .RequireAuthenticatedUser() // site-wide auth
        .Build();
    options.Filters.Add(new AuthorizeFilter(policy));
});

推荐答案

以下是该问题的完整且可行的解决方案:

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

首先,您需要提供一种身份验证质询请求机制,该机制可以重定向到身份验证代理(例如IdentityServer).这仅可通过HttpContext来实现,而HttpContext在SignalR(Blazor Server应用程序)中不可用.为了解决这个问题,我们将在HttpContext可用的地方添加几个Razor页面.答案中有更多...

First off, you'll need to provide an 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...

创建Blazor服务器应用程序.

Create a Blazor Server App.

安装软件包Microsoft.AspNetCore.Authentication.OpenIdConnect-版本3.1.0或更高版本.

Install-Package Microsoft.AspNetCore.Authentication.OpenIdConnect -Version 3.1.0 or later.

创建一个名为LoginDisplay(LoginDisplay.razor)的组件,并将其放置在共享文件夹.此组件在MainLayout组件中使用:

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>

将LoginDisplay组件添加到MainLayout组件中,位于About的正上方锚元素,像这样

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>

注意:为了将登录和注销请求重定向到IdentityServer,我们必须创建两个Razor页面,如下所示:

Note: In order to redirect requests for login and for logout to IdentityServer, we have to create two Razor pages as follows:

  1. 创建一个登录剃须刀"页面Login.cshtml(Login.cshtml.cs),并将其放置在Pages文件夹中,如下所示: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 } );
    }  
}

此代码对您在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


using Microsoft.AspNetCore.Authentication;

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

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

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

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

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>

使用以下代码替换Startup类中的代码:

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

重要:在以上所有代码示例中,您都必须根据需要添加using语句.默认情况下,大多数都是提供的.这里提供的使用是启用身份验证和授权流程所必需的.

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.

  • 运行您的应用程序,单击登录"按钮进行身份验证.您将被重定向到IdentityServer测试服务器,该服务器允许您执行OIDC登录.您可以输入用户名: bob 和密码 bob ,然后单击确定"按钮,您将被重定向到您的主页.另请注意,您可以使用外部登录提供商Google(尝试).请注意,在使用身份服务器登录后,LoginDisplay组件将显示字符串"您好,<您的用户名>" .
  • 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, <your user name>".

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

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

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

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

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

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.

NotAuthorized 元素中的代码如下:

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

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

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.

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

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

这篇关于服务器端Blazor中的OIDC身份验证的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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