爲了演示身份驗證如何在服務器端 Blazor 應用程序中工做,咱們將把身份驗證簡化爲最基本的元素。 咱們將簡單地設置一個 cookie,而後讀取應用程序中的 cookie。html
應用程序身份驗證ios
大多數商業 web 應用程序都要求用戶登陸到應用程序中。git
用戶輸入他們的用戶名和密碼,對照成員資格數據庫進行檢查。github
一旦經過身份驗證,該應用程序便可識別用戶,而且如今能夠安全地傳遞內容。web
理解了服務器端 Blazor 應用程序的身份驗證過程,咱們就能夠實現一個知足咱們須要的身份驗證和成員資格管理系統(例如,一個容許用戶建立和管理其用戶賬戶的系統)。數據庫
注意:此示例代碼不會檢查是否有人使用了合法的用戶名和密碼! 您將須要添加正確的代碼進行檢查。 這段代碼只是對受權用戶的過程的演示。api
建立應用程序瀏覽器
打開Visual Studio 2019。安全
建立沒有身份驗證的 Blazor 服務器應用程序。服務器
添加Nuget軟件包
在解決方案資源管理器中,右鍵單擊項目名稱並選擇 Manage NuGet Packages。
添加對如下庫的引用:
另外還有
添加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被刪除。
這段代碼還添加了:
在代碼中使用依賴注入訪問的服務。
查看這個連接能夠得到關於 httpcontexcessor 如何讓咱們肯定登陸用戶是誰的完整解釋。
添加登陸/註銷頁面
登陸(和註銷)由.cshtml頁面執行。
添加如下Razor頁面和代碼:
1 @page 2 @model BlazorCookieAuth.Server.Pages.LoginModel 3 @{ 4 ViewData["Title"] = "Log in"; 5 } 6 <h2>Login</h2>
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 }
1 @page 2 @model BlazorCookieAuth.Server.Pages.LogoutModel 3 @{ 4 ViewData["Title"] = "Logout"; 5 } 6 <h2>Logout</h2>
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 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)¶mPassword=@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中傳遞它便可。
如今,當咱們登陸並單擊「獲取用戶」按鈕時,控制器方法便可以檢測到已登陸的用戶。