簡單服務器端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中傳遞它即可。

現在,當我們登錄並單擊“獲取用戶”按鈕時,控制器方法便能夠檢測到已登錄的用戶。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM