相关文章推荐
另类的汤圆  ·  最后的召唤师漫画_第14回,重逢(5),第2 ...·  2 年前    · 
不敢表白的海豚  ·  神不会掷骰子完整,在线全集 - 快看·  2 年前    · 
小百科  ›  Blazor WebAssembly身份证书与许可权_blazor cn证书_JimCarter的博客-CSDN博客
前端组件 webassembly jwt
礼貌的消炎药
2 年前
  • 1.简介
  • 2. 使用OIDC进行身份验证的流程
  • 3. 授权
  • 4. 验证库的使用
    • 4.1 配置依赖注入
    • 4.2 添加命令空间和js
    • 4.3 添加组件
      • 4.3.1 配置
      • 4.3.2 `RedirectToLogin`组件
      • 4.3.3 `LoginDisplay`组件
    • 5.常见组件和服务
      • 5.1 `Authentication`组件
      • 5.2 `AuthorizeView` 组件
      • 5.3 `AuthenticationStateProvider` 服务
        • 5.3.1 自定义`AuthenticationStateProvider`服务
      • 6. `[Authorize]`特性
      • 7. 其它说明

      Blazor Server基于SignalR,建立连接之后可以通过cookie进行验证,配合 Asp.Net Core Identity 使用。

      Blazor WebAssembly 应用的保护方式与单页应用 (SPA) 相同。 可通过多种方式向 SPA 进行用户身份验证,但最常用、最全面的方式是使用基于 OAuth 2.0 协议的实现,例如 OpenID Connect (OIDC)。

      如果需要使用OIDC对应用进行身份验证和授权,需要安装在wasm里安装Nuget包 Microsoft.AspNetCore.Components.WebAssembly.Authentication 。

      (安装的前提是你的blazor项目需要用 aps.net core 作为host。这个包用于处理基础身份验证协议,建立在 oidc-client.js 库基础之上。)

      当然除了使用OIDC进行验证和授权之外,还可以使用SameSite cookie等。但是blazor wasm的设计上就决定了使用OAuth和OIDC是进行身份验证过的最佳选择。出于以下几个原因,我们这里使用JWT(Json Web Token)进行身份验证而不是使用cookie:

      • 可以减小攻击面,因为并非所有请求中都会发送令牌
      • 服务器终结点不要求针对跨站点请求伪造 (CSRF) 进行保护,因为会显式发送令牌。所以可将 Blazor WebAssembly 应用与 MVC 或 Razor Pages 应用一起托管
      • 令牌的权限比 cookie 窄。例如,令牌不能用于管理用户帐户或更改用户密码
      • 令牌的生命周期更短(默认为一小时),这限制了攻击时间窗口。还可随时撤销令牌。

      JWT分为三部分 {header}.{payload}.{signature} , 解码之后各部分格式和含义如下:

      "typ" : "JWT" , "alg" : "RS256" , "kid" : "X5eXk4xyojNFum1kl2Ytv8dlNP4-c57dO6QGTVBwaNk" } . { "exp" : 1610059429 , "nbf" : 1610055829 , "ver" : "1.0" , "iss" : "https://mysiteb2c.b2clogin.com/5cc15ea8-a296-4aa3-97e4-226dcc9ad298/v2.0/" , "sub" : "5ee963fb-24d6-4d72-a1b6-889c6e2c7438" , "aud" : "70bde375-fce3-4b82-984a-b247d823a03f" , "nonce" : "b2641f54-8dc4-42ca-97ea-7f12ff4af871" , "iat" : 1610055829 , "auth_time" : 1610055822 , "idp" : "idp.com" , "tfp" : "B2C_1_signupsignin" , //还可以 自定义字段 } . [ Signature ]
      字段 说明
      typ token的类型:jwt
      alg 所使用的加密算法
      kid 秘钥序,开发人员可以用它标识认证token的某一秘钥
      exp 过期时间
      nbf 在这个时间点之前,jwt都是不可用的
      ver 版本
      iss jwt的签发者
      sub jwt所面向的用户
      aud 接收jwt的一方
      nonce
      iat jwt签发时间
      auth_time
      idp
      tfp
      jti jwt的唯一身份标识,主要用来作为一次性token,回避重放攻击

      2. 使用OIDC进行身份验证的流程

      请先新建一个带有认证功能的Blazor WebAssembly项目便于接下来的理解,该模板的host项目已经添加了IdentityServer服务
      在这里插入图片描述
      验证流程:

      1. 当未登陆的用户点击了登陆按钮或者请求到应用了 [Authorize] 特性的页面上时,就会将该用户重定向到 /authentication/login 。
      2. 在登陆页上,身份验证库 Microsoft.AspNetCore.Components.WebAssembly.Authentication 将会把请求重定向到授权服务上(接下来的文章中会介绍如何使用IdentityServer搭建授权服务)。该授权服务负责确定用户是否通过身份验证,并发送token作为响应。
        • 如果用户未通过身份验证,则会提示让用户进行登录。此处可以配合 ASP.NET Core Identity 使用
        • 如果用户已通过身份验证,则授权服务生成相应的token,并将浏览器重定向到 /authentication/login-callback 。
      3. 当Blazor应用加载 /authentication/login-callback 时,就处理了身份验证相应。
        • 如果身份验证成功,则可以选择将用户重定向到原始访问的url上。
        • 如果因为任何原因验证失败,则会将用户重定向到 authentication/login-failed ,并显示错误。

      总结:整个工作流程涉及到三个url,这三个其实都在 Shared/Authentication.razor 里。:

      1. /authentication/login 页
      2. /authentication/login-callback
      3. /authentication/login-failed

      该nuget包用下表中显示的路由表示不同的身份验证状态。

      路由 目标
      authentication/login 触发登录操作。
      authentication/login-callback 处理任何登录操作的结果。
      authentication/login-failed 当登录操作由于某种原因失败时显示错误消息。
      authentication/logout 触发注销操作。
      authentication/logout-callback 处理注销操作的结果。
      authentication/logout-failed 当注销操作由于某种原因失败时显示错误消息。
      authentication/logged-out 指示用户已成功注销。
      authentication/profile 触发操作以编辑用户配置文件。
      authentication/register 触发操作以注册新用户。

      3. 授权

      对用户验证通过之后,就需要验证授权规则来控制用户可以具体执行哪些操作。常见的授权规则有以下几种:

      • 只要用户通过验证就授权
      • 基于角色的授权
      • 基于用户claim的授权
      • 基于策略的授权

      4. 验证库的使用

      4.1 配置依赖注入

      在WebAssembly项目中(非host),使用 Microsoft.AspNetCore.Components.WebAssembly.Authentication 包提供的 AddOidcAuthentication 方法在服务容器中注册用户身份验证支持。这里以Google的OIDC服务为例进行配置:

      builder.Services.AddOidcAuthentication(options =>
          //从appsettings.json里读取Local配置项并进行设置
          builder.Configuration.Bind("Local", options.ProviderOptions);
      });
      

      appsettings.json Local配置项:

      "Local": { "Authority": "https://accounts.google.com/", "ClientId": "2.......7-e.....................q.apps.googleusercontent.com", "PostLogoutRedirectUri": "https://localhost:5001/authentication/logout-callback", "RedirectUri": "https://localhost:5001/authentication/login-callback", "ResponseType": "id_token"

      4.2 添加命令空间和js

      1. 在_Imports.razor里添加@using Microsoft.AspNetCore.Components.Authorization。

      2. 在wwwroot/index.html里添加<script src="_content/Microsoft.AspNetCore.Components.WebAssembly.Authentication/ AuthenticationService.js"></script>。这个js用来处理OIDC的细节,应用内部会调用这个服务。

      4.3 添加组件

      4.3.1 配置

      1. CascadingAuthenticationState组件:用来提供经过验证的用户的信息。
      2. AuthorizaRouteView组件:用来确保用户是否可以访问给定的页面,如果未被授权则渲染RedirectToLogin组件。
      3. RedirectToLogin组件:用来将用户重定向到登录页。

      整体代码如下:

      <CascadingAuthenticationState>
          <Router AppAssembly="@typeof(Program).Assembly">
              <Found Context="routeData">
                  <AuthorizeRouteView
      
      
      
      
          
       RouteData="@routeData" 
                      DefaultLayout="@typeof(MainLayout)">
                      <NotAuthorized>
                          @if (!context.User.Identity.IsAuthenticated)
                              <RedirectToLogin />
                                  You are not authorized to access 
                                  this resource.
                      </NotAuthorized>
                  </AuthorizeRouteView>
              </Found>
              <NotFound>
                  <LayoutView Layout="@typeof(MainLayout)">
                      <p>Sorry, there's nothing at this address.</p>
                  </LayoutView>
              </NotFound>
          </Router>
      </CascadingAuthenticationState>
      

      4.3.2 RedirectToLogin组件

      Shared/RedirectToLogin.razor组件,用来引导用户进行登录,传入验证成功之后的跳转页。

      @inject NavigationManager Navigation
      @using Microsoft.AspNetCore.Components.WebAssembly.Authentication
      @code {
          protected override void OnInitialized()
              Navigation.NavigateTo(
                  $"authentication/login?returnUrl={Uri.EscapeDataString(Navigation.Uri)}");
      

      4.3.3 LoginDisplay组件

      Shared/LoginDisplay.razor组件。对于验证过的用户来说显示用户名等信息,并提供注销功能。对于未验证的用户则提供登录功能。

      @using Microsoft.AspNetCore.Components.Authorization
      @using Microsoft.AspNetCore.Components.WebAssembly.Authentication
      @inject NavigationManager Navigation
      @inject SignOutSessionStateManager SignOutManager
      <AuthorizeView>
          <Authorized>
              Hello, @context.User.Identity.Name!
              <button class="nav-link btn btn-link" @onclick="BeginSignOut">
                  Log out
              </button>
          </Authorized>
          <NotAuthorized>
              <a href="authentication/login">Log in</a>
          </NotAuthorized>
      </AuthorizeView>
      @code {
          private async Task BeginSignOut(MouseEventArgs args)
              await SignOutManager.SetSignOutState();
              Navigation.NavigateTo("authentication/logout");
      

      5.常见组件和服务

      5.1 Authentication组件

      Pages/Authentication.razor下的RemoteAuthenticatorView(属于nuget包),用来处理不同的验证步骤。

      @page "/authentication/{action}"
      @using Microsoft.AspNetCore.Components.WebAssembly.Authentication
      <RemoteAuthenticatorView Action="@Action" />
      @code {
          [Parameter]
          public string Action { get; set; }
      

      5.2 AuthorizeView 组件

      该组件可以根据用户是否经过授权来显示不同的UI,常用来设置页面内某个部分是否可见。该组件公开了一个AuthenticationState类型的变量context,可以使用该变量获取已登录的用户信息。

      <AuthorizeView>
          <Authorized>
              <h1>Hello, @context.User.Identity.Name!</h1>
              <p>You can only see this content if you're authorized.</p>
              <button @onclick="SecureMethod">Authorized Only Button</button>
          </Authorized>
          <NotAuthorized>
              <h1>Authentication Failure!</h1>
              <p>You're not signed in.</p>
          </NotAuthorized>
          <Authorizing>
              <h1>Authentication in progress</h1>
              <p>You can only see this content while authentication is in progress.</p>
          </Authorizing>
      </AuthorizeView>
      @code {
          private void SecureMethod() { ... }
      

      如果未指定授权规则,则表示用户验证通过就表示已授权。

      AuthorizeView支持基于角色或基于策略的授权。配置如下:

      <AuthorizeView Roles="admin, superuser">
          <p>You can only see this if you're an admin or superuser.</p>
      </AuthorizeView>
      <AuthorizeView Policy="content-editor">
          <p>You can only see this if you satisfy the "content-editor" policy.</p>
      </AuthorizeView>
      

      基于策略的授权包含一个特例,即基于claim的授权。例如,可以定义一个要求用户具有某种claim的策略

      5.3 AuthenticationStateProvider 服务

      AuthenticationStateProvider是AuthorizeView和CascadingAuthenticationState组件用于获取身份验证状态的基础服务。我们一般不直接使用这个,主要是因为当基础身份验证状态发生改变时不会自动通知UI组件。

      可以通过AuthenticationStateProvider服务来获取用户的Claim数据:

      @page "/"
      @using System.Security.Claims
      @using Microsoft.AspNetCore.Components.Authorization
      @inject AuthenticationStateProvider AuthenticationStateProvider
      <h3>ClaimsPrincipal Data</h3>
      <button @onclick="GetClaimsPrincipalData">Get ClaimsPrincipal Data</button>
      <p>@_authMessage</p>
      @if (_claims.Count() > 0)
              @foreach (var claim in _claims)
                  <li>@claim.Type: @claim.Value</li>
      <p>@_surnameMessage</p>
      @code {
          private string _authMessage;
          private string _surnameMessage;
          private IEnumerable<Claim> _claims = Enumerable.Empty<Claim>();
          private async Task GetClaimsPrincipalData()
              var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
              var user = authState.User;
              if (user.Identity.IsAuthenticated)
                  _authMessage = $"{user.Identity.Name} is authenticated.";
                  _claims = user.Claims;
                  _surnameMessage = 
                      $"Surname: {user.FindFirst(c => c.Type == ClaimTypes.Surname)?.Value}";
                  _authMessage = "The user is NOT authenticated.";
      

      刚才我们说了一般不要直接使用AuthenticationStateProvider,但是如果真要在页面中获取验证状态该怎么办?

      答案就是定义一个Task<AuthenticationState>类型的级联参数,父级的AuthorizeRouteView或CascadingAuthenticationState组件,会给这个参数赋值。反过来CascadingAuthenticationState会从AuthenticationStateProvider服务接收这个参数 。

      @page "/"
      <button @onclick="LogUsername">Log username</button>
      <p>@_authMessage</p>
      @code {
          [CascadingParameter]
          private Task<AuthenticationState> authenticationStateTask { get; set; }
          private string _authMessage;
          private async Task LogUsername()
              var authState = await authenticationStateTask;
              var user = authState.User;
              if (user.Identity.IsAuthenticated)
                  _authMessage = $"{user.Identity.Name} is authenticated.";
                  _authMessage = "The user is NOT authenticated.";
      

      然后在配置依赖注入:

      builder.Services.AddOptions();
      builder.Services.AddAuthorizationCore();
      

      5.3.1 自定义AuthenticationStateProvider服务

      重写GetAuthenticationStateAsync方法即可:

      using System.Security.Claims;
      using System.Threading.Tasks;
      using Microsoft.AspNetCore.Components.Authorization;
      public class CustomAuthStateProvider : AuthenticationStateProvider
          public override Task<AuthenticationState> GetAuthenticationStateAsync()
              var identity = new ClaimsIdentity(new[]
                  new Claim(ClaimTypes.Name, "mrfibuli"),
              }, "Fake authentication type");
              var user = new ClaimsPrincipal(identity);
              return Task.FromResult(new AuthenticationState(user));
      

      然后配置依赖注入

      builder.Services.AddScoped<AuthenticationStateProvider, CustomAuthStateProvider>();
      

      6. [Authorize]特性

      表示用户经过授权之后才可以访问页面,与AuthorizeView区别是一个属于页面级,一个属于UI块。

      1. 对所有的页面都需要进行授权:在_Imports.razor文件中使用Authorize特性。
      @using Microsoft.AspNetCore.Authorization
      @attribute [Authorize]
      
      1. 对某个页面进行授权:在页面上添加@attribute [Authorize]。

      基于角色和策略的授权:

      @attribute [Authorize(Roles = "admin, superuser")]
      @attribute [Authorize(Policy = "content-editor")]
      

      7. 其它说明

      1. 不可以在Blaozr WASM应用中保存刷新令牌。只能在托管的host里保存。
      2. Access Token的作用域:验证库默认会添加openid和profile这两个scope。如果需要添加其它的scope可以调用:
      builder.Services.AddOidcAuthentication(options =>
          ...
          options.ProviderOptions.DefaultScopes.Add("ScopeXXXXX");//输入scope
      });
      

      下一篇将会介绍如何与IdentityServer集成。

      1. https://docs.microsoft.com/zh-cn/aspnet/core/blazor/security/webassembly/?view=aspnetcore-5.0#authentication-component
      2. https://docs.microsoft.com/zh-cn/aspnet/core/blazor/security/webassembly/standalone-with-authentication-library?view=aspnetcore-5.0&tabs=visual-studio
      3. https://docs.microsoft.com/zh-cn/aspnet/core/blazor/security/?view=aspnetcore-5.0#authorizeview-component
      将Blazor部署到GitHub Pages 该通过gh-pages分支将存储库中的Blazor WebAssembly项目发布并部署到 。 可以在示例中看到它。 这将自动执行所有必需的设置,包括添加.nojekyll文件和404.html页面重定向解决方法,这对于使单页面应用程序在GitHub Pages中工作是必需的。 这基于的示例。 - uses : proulxsamuel/deploy-blazor-to-gh-pages@master with : # The path to the Blazor project in the repository. # Default: '.' project_path : ' ' 这要求您在存储库设置中启用GitHub Pages,并首先通过存储库。 您可以查看工作流程示例。 之前用 Blazor 做了几个小应用,但是一直没有去做身份验证系统,之前也没有ASP.NET的基础,C#也是最近学的,而且重要的是,网上居然几乎没有相关的资料,于是咱就结合网上的一些信息和微软的官方文档(微软的文档是我见过最好的文档了)做了一个身份鉴权的小Demo。在 Provider 下写自定义的基础认证服务,这里使用的session的形式做鉴权认证。Blazor 是微软的新型的web开发方案,用来做全栈开发真是太爽了。然后在项目的目录下创建Auth目录存放鉴权的基础设施。一个简单的登录页面示例; 如何使用这个包 对于 v0.*.* 很重要: 此包仍处于预发布阶段,因此版本控制不符合语义版本控制。 功能和错误修复会增加补丁版本,重大更改会增加次要版本。 因此,在次要版本之间升级之前,请务必查看发行说明。 这个包导入了另外两个包,它们是: - 为 WebExtensions 标准 API 提供互操作。 Blazor.BrowserExtension.Build(在此存储库中)- 向项目添加构建目标和任务。 创建新项目 运行dotnet new --install Blazor.BrowserExtension.Template 。 运行dotnet new browserext --name <ProjectName>以使用模板初始化新项目。 将工作目录更改为新创建的 Blazo的身份认证授权示例以及如何对接统一登录平台     在阅读本文之前,希望您已经对ASP.NET Core或者Web应用的身份认证机制有所了解。本文主要讲述Blazor WebAssembly模式下的身份认证和授权方法,以及页面元素访问控制,假如您对ASP.NET Core身份认证机制还不了解,可以查看该链接获取完整的sso——统一登录示例。 AuthorizeView 组件 AuthorizeView是blazor中用于身份认证的内置组件。 <AuthorizeView> 注意:Key要求有一定的长度,建议复制新生成的Guid.NewGuid().ToString();上篇已经实现了登录与退出的主服务类,其他还需要一些具体的东西。1、引用Blazored.LocalStorage包;7、登录Razor组件(略)。注意:需要注册服务。... 本文要点WebAssembly 是一种新的客户端技术,可以在所有现代浏览器(包括移动浏览器)中实现近乎原生的性能,而且不需要插件。许多语言,包括 C、C#、Go 和 Ru... Blazor WebAssembly Blazor WebAssembly 应用在客户端上运行。 由于用户可绕过客户端检查,因为用户可修改所有客户端代码, 因此授权仅用于确定要显示的 UI 选项,所有客户端应用程序技术都是如此。 Blazor Server Blazor Server应用通过使用 SignalR 创建的实时连接运行。 建立连接后,将处理基于 Sig 通过前面的ASP.NET Core Blazor编程系列文章为读者介绍了Blazor及组件的相关基础概念,以及我们已经实现了用Blazor实现对数据的增删改查这四大基本功能,以及文件上传的功能,通过这些功能的实现我们已经能用Blazor处理一些简单的实际问题,特别是企业内部信息管理系统的相关问题。前面的ASP.NET Core Blazor编程系列文章中却没有讲到信息管理系统中一个最基本功能——登录,有关登录功能的介绍。 通过使用客户端和服务器上的 .NET,你可以轻松共享代码,并使用一组一致的语言、框架和工具生成应用。生成解决方案后,Blazor 应用中已生成的静态文件由已设置回退路由的 ASP.NET Core 应用托管。下载的程序集是普通的 .NET 程序集,就像在任何其他 .NET 应用中使用的程序集一样。到应用的某些部分的深层链接通常需要服务器上的路由解决方案。Blazor 组件根据呈现的输出的应用方式进行去耦。在 Blazor Server 应用中,组件在服务器上运行,而不是在浏览器中的客户端运行。 在Blazor WebAssembly开发模式下, 浏览器一般需要下载多达10M的资源, 主要是 .net 的dll 文件, 即使是在企业局域网内访问速度也不快. 发布模式可以明显减小资源文件的大小. 这里介绍使用官方提供的 blazor-devserver.exe, 作为企业内的部署服务器, 主要优点有: ⒈ 使用非常方便 2. 支持url rewrite, 以模版项目为例, counter... 本文将从0开始介绍如何搭建一个适用于Blazor WASM应用的且基于OpenID和OAuth2.0的认证授权服务。我们会从创建空白项目一步一步开始,让大家了解到整个搭建流程,没有直接使用微软给定的认证模板或者IdentityServer的UI模板。 - 前端使用的是Blazor WebAssembly最新版本(基于 .net 5.0),主要我认为相对于Blazor Server来说,这个才是Web的未来。而且可以充分开会客户机的性能。 - 认证服务使用的Duende.IdentityServer Blazor页面元素授权——AuthorizeView 组件的使用 上篇博客我们说到了blazor的身份认证的实现,对于AuthorizeView 组件来说,可以通过级联参数来获取包含了用户信息的AuthenticationState对象。 请注意,你需要引用Microsoft.AspNetCore.Components.Authorization Nuget包,并且在启动类中添加服务Services.AddAuthorizationCore(); 使用CascadingAuthenticationStat
 
推荐文章
另类的汤圆  ·  最后的召唤师漫画_第14回,重逢(5),第21页_在线漫画阅读_动漫屋
2 年前
不敢表白的海豚  ·  神不会掷骰子完整,在线全集 - 快看
2 年前
今天看啥   ·   Py中国   ·   codingpro   ·   小百科   ·   link之家   ·   卧龙AI搜索
删除内容请联系邮箱 2879853325@qq.com
小百科 - 百科知识指南
© 2024 ~ 沪ICP备11025650号