• 简单服务器端Blazor Cookie身份验证的演示


    为了演示身份验证如何在服务器端 Blazor 应用程序中工作,我们将把身份验证简化为最基本的元素。 我们将简单地设置一个 cookie,然后读取应用程序中的 cookie。

    应用程序身份验证

    大多数商业 web 应用程序都要求用户登录到应用程序中。

    用户输入他们的用户名和密码,对照成员资格数据库进行检查。

    一旦通过身份验证,该应用程序即可识别用户,并且现在可以安全地传递内容。

    理解了服务器端 Blazor 应用程序的身份验证过程,我们就可以实现一个满足我们需要的身份验证和成员资格管理系统(例如,一个允许用户创建和管理其用户帐户的系统)。

    注意:此示例代码不会检查是否有人使用了合法的用户名和密码! 您将需要添加正确的代码进行检查。 这段代码只是对授权用户的过程的演示。

    创建应用程序

    打开Visual Studio 2019。

    创建没有身份验证的 Blazor 服务器应用程序。

    添加Nuget软件包

    在解决方案资源管理器中,右键单击项目名称并选择 Manage NuGet Packages。

    添加对以下库的引用:

    • Microsoft.AspNetCore.Authorization
    • Microsoft.AspNetCore.Http
    • Microsoft.AspNetCore.Identity

    另外还有

    • Microsoft.AspNetCore.Blazor.HttpClient

    添加Cookie身份验证

    打开Startup.cs文件。

    在文件顶部添加以下using语句:

    1 // ******
    2 // BLAZOR COOKIE Auth Code (begin)
    3 using Microsoft.AspNetCore.Authentication.Cookies;
    4 using Microsoft.AspNetCore.Http;
    5 using System.Net.Http;
    6 // BLAZOR COOKIE Auth Code (end)
    7 // ******

    将Start 类改为如下,添加注释标记为 BLAZOR COOKIE Auth Code 的部分:

     1 public class Startup
     2     {
     3         public Startup(IConfiguration configuration)
     4         {
     5             Configuration = configuration;
     6         }
     7         public IConfiguration Configuration { get; }
     8         // This method gets called by the runtime. Use this method to 
     9         // add services to the container.
    10         // For more information on how to configure your application, 
    11         // visit https://go.microsoft.com/fwlink/?LinkID=398940
    12         public void ConfigureServices(IServiceCollection services)
    13         {
    14             // ******
    15             // BLAZOR COOKIE Auth Code (begin)
    16             services.Configure<CookiePolicyOptions>(options =>
    17             {
    18                 options.CheckConsentNeeded = context => true;
    19                 options.MinimumSameSitePolicy = SameSiteMode.None;
    20             });
    21             services.AddAuthentication(
    22                 CookieAuthenticationDefaults.AuthenticationScheme)
    23                 .AddCookie();
    24             // BLAZOR COOKIE Auth Code (end)
    25             // ******
    26             services.AddRazorPages();
    27             services.AddServerSideBlazor();
    28             services.AddSingleton<WeatherForecastService>();
    29             // ******
    30             // BLAZOR COOKIE Auth Code (begin)
    31             // From: https://github.com/aspnet/Blazor/issues/1554
    32             // HttpContextAccessor
    33             services.AddHttpContextAccessor();
    34             services.AddScoped<HttpContextAccessor>();
    35             services.AddHttpClient();
    36             services.AddScoped<HttpClient>();
    37             // BLAZOR COOKIE Auth Code (end)
    38             // ******
    39         }
    40         // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    41         public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    42         {
    43             if (env.IsDevelopment())
    44             {
    45                 app.UseDeveloperExceptionPage();
    46             }
    47             else
    48             {
    49                 app.UseExceptionHandler("/Error");
    50                 // The default HSTS value is 30 days. 
    51                 // You may want to change this for production scenarios, 
    52                 // see https://aka.ms/aspnetcore-hsts.
    53                 app.UseHsts();
    54             }
    55             app.UseHttpsRedirection();
    56             app.UseStaticFiles();
    57             app.UseRouting();
    58             // ******
    59             // BLAZOR COOKIE Auth Code (begin)
    60             app.UseHttpsRedirection();
    61             app.UseStaticFiles();
    62             app.UseCookiePolicy();
    63             app.UseAuthentication();
    64             // BLAZOR COOKIE Auth Code (end)
    65             // ******
    66             app.UseEndpoints(endpoints =>
    67             {
    68                 endpoints.MapBlazorHub();
    69                 endpoints.MapFallbackToPage("/_Host");
    70             });
    71         }
    72     }

    首先,代码添加了对cookie的支持。 Cookie由应用程序创建,并在用户登录时传递到用户的Web浏览器。Web浏览器将Cookie传递回应用程序以指示用户已通过身份验证。 当用户“注销”时,cookie被删除。

    这段代码还添加了:

    • HttpContextAccessor
    • HttpClient

    在代码中使用依赖注入访问的服务。

    查看这个链接可以获得关于 httpcontexcessor 如何让我们确定登录用户是谁的完整解释。

    添加登录/注销页面

    登录(和注销)由.cshtml页面执行。

    添加以下Razor页面和代码:

    Login.cshtml

    1 @page
    2 @model BlazorCookieAuth.Server.Pages.LoginModel
    3 @{
    4     ViewData["Title"] = "Log in";
    5 }
    6 <h2>Login</h2>

    Login.cshtml.cs

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Security.Claims;
     4 using System.Threading.Tasks;
     5 using Microsoft.AspNetCore.Authentication;
     6 using Microsoft.AspNetCore.Authentication.Cookies;
     7 using Microsoft.AspNetCore.Authorization;
     8 using Microsoft.AspNetCore.Mvc;
     9 using Microsoft.AspNetCore.Mvc.RazorPages;
    10 namespace BlazorCookieAuth.Server.Pages
    11 {
    12     [AllowAnonymous]
    13     public class LoginModel : PageModel
    14     {
    15         public string ReturnUrl { get; set; }
    16         public async Task<IActionResult> 
    17             OnGetAsync(string paramUsername, string paramPassword)
    18         {
    19             string returnUrl = Url.Content("~/");
    20             try
    21             {
    22                 // 清除现有的外部Cookie
    23                 await HttpContext
    24                     .SignOutAsync(
    25                     CookieAuthenticationDefaults.AuthenticationScheme);
    26             }
    27             catch { }
    28             // *** !!! 在这里您可以验证用户 !!! ***
    29             // 在此示例中,我们仅登录用户(此示例始终登录用户)
    30             //
    31             var claims = new List<Claim>
    32             {
    33                 new Claim(ClaimTypes.Name, paramUsername),
    34                 new Claim(ClaimTypes.Role, "Administrator"),
    35             };
    36             var claimsIdentity = new ClaimsIdentity(
    37                 claims, CookieAuthenticationDefaults.AuthenticationScheme);
    38             var authProperties = new AuthenticationProperties
    39             {
    40                 IsPersistent = true,
    41                 RedirectUri = this.Request.Host.Value
    42             };
    43             try
    44             {
    45                 await HttpContext.SignInAsync(
    46                 CookieAuthenticationDefaults.AuthenticationScheme,
    47                 new ClaimsPrincipal(claimsIdentity),
    48                 authProperties);
    49             }
    50             catch (Exception ex)
    51             {
    52                 string error = ex.Message;
    53             }
    54             return LocalRedirect(returnUrl);
    55         }
    56     }
    57 }

    Logout.cshtml

    1 @page
    2 @model BlazorCookieAuth.Server.Pages.LogoutModel
    3 @{
    4     ViewData["Title"] = "Logout";
    5 }
    6 <h2>Logout</h2>

    Logout.cshtml.cs

     1 using System;
     2 using System.Threading.Tasks;
     3 using Microsoft.AspNetCore.Authentication;
     4 using Microsoft.AspNetCore.Authentication.Cookies;
     5 using Microsoft.AspNetCore.Mvc;
     6 using Microsoft.AspNetCore.Mvc.RazorPages;
     7 namespace BlazorCookieAuth.Server.Pages
     8 {
     9     public class LogoutModel : PageModel
    10     {
    11         public async Task<IActionResult> OnGetAsync()
    12         {
    13             // 清除现有的外部Cookie
    14             await HttpContext
    15                 .SignOutAsync(
    16                 CookieAuthenticationDefaults.AuthenticationScheme);
    17             return LocalRedirect(Url.Content("~/"));
    18         }
    19     }
    20 }

    添加客户代码

    使用以下代码将一个名为 LoginControl.razor 的页面添加到 Shared 文件夹:

     1 @page "/loginControl"
     2 @using System.Web;
     3 <AuthorizeView>
     4     <Authorized>
     5         <b>Hello, @context.User.Identity.Name!</b>
     6         <a class="ml-md-auto btn btn-primary"
     7            href="/logout?returnUrl=/"
     8            target="_top">Logout</a>
     9     </Authorized>
    10     <NotAuthorized>
    11         <input type="text"
    12                placeholder="User Name"
    13                @bind="@Username" />
    14         &nbsp;&nbsp;
    15         <input type="password"
    16                placeholder="Password"
    17                @bind="@Password" />
    18         <a class="ml-md-auto btn btn-primary"
    19            href="/login?paramUsername=@encode(@Username)&paramPassword=@encode(@Password)"
    20            target="_top">Login</a>
    21     </NotAuthorized>
    22 </AuthorizeView>
    23 @code {
    24     string Username = "";
    25     string Password = "";
    26     private string encode(string param)
    27     {
    28         return HttpUtility.UrlEncode(param);
    29     }
    30 }

    此代码创建一个登录组件,该组件使用AuthorizeView组件根据用户当前的身份验证包装标记代码。

    如果用户已登录,我们将显示其姓名和一个“注销”按钮(可将用户导航到之前创建的注销页面)。

    如果未登录,我们会显示用户名和密码框以及一个登录按钮(将用户导航到之前创建的登录页面)。

    最后,我们将MainLayout.razor页面(在Shared文件夹中)更改为以下内容:

     1 @inherits LayoutComponentBase
     2 <div class="sidebar">
     3     <NavMenu />
     4 </div>
     5 <div class="main">
     6     <div class="top-row px-4">
     7         <!-- BLAZOR COOKIE Auth Code (begin) -->
     8         <LoginControl />
     9         <!-- BLAZOR COOKIE Auth Code (end) -->
    10     </div>
    11     <div class="content px-4">
    12         @Body
    13     </div>
    14 </div>

    这会将登录组件添加到Blazor应用程序中每个页面的顶部。

    打开App.razor页面,并将所有现有代码包含在 CascadingAuthenticationState 标记中。

    现在我们可以按F5键运行该应用程序。

    我们可以输入用户名和密码,然后单击“登录”按钮…

    然后我们可以在 Google Chrome 浏览器 DevTools 中看到 cookie 已经被创建。

    当我们单击注销...

    Cookie被删除。

    调用服务器端控制器方法

    此时,所有.razor页面将正确检测用户是否已通过身份验证,并按预期运行。 但是,如果我们向服务器端控制器发出http请求,则将无法正确检测到经过身份验证的用户。

    为了演示这一点,我们首先打开startup.cs页面,并将以下代码添加到app.UseEndpoints方法的末尾(在endpoints.MapFallbackToPage(“/ _ Host”)行下),以允许对控制器的http请求 正确路由:

    1  // ******
    2  // BLAZOR COOKIE Auth Code (begin)
    3     endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
    4  // BLAZOR COOKIE Auth Code (end)
    5  // ******

    接下来,我们创建一个Controllers文件夹,并使用以下代码添加UserController.cs文件:

     1 using Microsoft.AspNetCore.Mvc;
     2 namespace BlazorCookieAuth.Controllers
     3 {
     4     [Route("api/[controller]")]
     5     [ApiController]
     6     public class UserController : Controller
     7     {
     8         // /api/User/GetUser
     9         [HttpGet("[action]")]
    10         public UserModel GetUser()
    11         {
    12             // Instantiate a UserModel
    13             var userModel = new UserModel
    14             {
    15                 UserName = "[]",
    16                 IsAuthenticated = false
    17             };
    18             // Detect if the user is authenticated
    19             if (User.Identity.IsAuthenticated)
    20             {
    21                 // Set the username of the authenticated user
    22                 userModel.UserName = 
    23                     User.Identity.Name;
    24                 userModel.IsAuthenticated = 
    25                     User.Identity.IsAuthenticated;
    26             };
    27             return userModel;
    28         }
    29     }
    30     // Class to hold the UserModel
    31     public class UserModel
    32     {
    33         public string UserName { get; set; }
    34         public bool IsAuthenticated { get; set; }
    35     }
    36 }

    我们使用以下代码添加一个新的.razor页面CallServerSide.razor:

     1 @page "/CallServerSide"
     2 @using BlazorCookieAuth.Controllers
     3 @using System.Net.Http
     4 @inject HttpClient Http
     5 @inject NavigationManager UriHelper
     6 @inject Microsoft.AspNetCore.Http.IHttpContextAccessor HttpContextAccessor
     7 <h3>Call Server Side</h3>
     8 <p>Current User: @CurrentUser.UserName</p>
     9 <p>IsAuthenticated: @CurrentUser.IsAuthenticated</p>
    10 <button class="btn btn-primary" @onclick="GetUser">Get User</button>
    11 @code {
    12     UserModel CurrentUser = new UserModel();
    13     async Task GetUser()
    14     {
    15         // Call the server side controller
    16         var url = UriHelper.ToAbsoluteUri("/api/User/GetUser");
    17         var result = await Http.GetJsonAsync<UserModel>(url.ToString());
    18         // Update the result
    19         CurrentUser.UserName = result.UserName;
    20         CurrentUser.IsAuthenticated = result.IsAuthenticated;
    21     }
    22 }

    最后,我们使用以下代码在Shared / NavMenu.razor中添加指向页面的链接:

    1 <li class="nav-item px-3">
    2       <NavLink class="nav-link" href="CallServerSide">
    3             <span class="oi oi-list-rich" aria-hidden="true"></span> Call Server Side
    4       </NavLink>
    5 </li>

    我们运行该应用程序并登录。

    我们导航到新的Call Server Side控件,然后单击Get User按钮(该按钮将调用刚刚添加的UserController.cs),并且它不会检测到已登录的用户。

    要解决此问题,请将CallServerSide.razor页面中的GetUser方法更改为以下内容:

     1 async Task GetUser()
     2     {
     3         // Code courtesy from Oqtane.org (@sbwalker)
     4         // We must pass the authentication cookie in server side requests
     5         var authToken =
     6         HttpContextAccessor.HttpContext.Request.Cookies[".AspNetCore.Cookies"];
     7         if (authToken != null)
     8         {
     9             Http.DefaultRequestHeaders
    10             .Add("Cookie", ".AspNetCore.Cookies=" + authToken);
    11             // Call the server side controller
    12             var url = UriHelper.ToAbsoluteUri("/api/User/GetUser");
    13             var result = await Http.GetJsonAsync<UserModel>(url.ToString());
    14             // Update the result
    15             CurrentUser.UserName = result.UserName;
    16             CurrentUser.IsAuthenticated = result.IsAuthenticated;
    17         }
    18     }

    我们有一个身份验证cookie,我们只需要在DefaultRequestHeaders中传递它即可。

    现在,当我们登录并单击“获取用户”按钮时,控制器方法便能够检测到已登录的用户。

  • 相关阅读:
    Prometheus学习系列(九)之Prometheus 存储
    Prometheus学习系列(八)之Prometheus API说明
    SSE图像算法优化系列七:基于SSE实现的极速的矩形核腐蚀和膨胀(最大值和最小值)算法。
    Crimm Imageshop 2.3。
    【短道速滑一】OpenCV中cvResize函数使用双线性插值缩小图像到长宽大小一半时速度飞快(比最近邻还快)之异象解析和自我实现。
    【算法随记七】巧用SIMD指令实现急速的字节流按位反转算法。
    【算法随记六】一段Matlab版本的Total Variation(TV)去噪算法的C语言翻译。
    SSE图像算法优化系列三十:GIMP中的Noise Reduction算法原理及快速实现。
    一种快速简便优秀的全局曲线调整与局部信息想结合的非线性彩色增强算法(多图深度分析和探索)
    【算法随记五】使用FFT变换自动去除图像中严重的网纹。
  • 原文地址:https://www.cnblogs.com/bisslot/p/12444967.html
Copyright © 2020-2023  润新知