为了演示身份验证如何在服务器端 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
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中传递它即可。
现在,当我们登录并单击“获取用户”按钮时,控制器方法便能够检测到已登录的用户。
原文出处:https://www.cnblogs.com/bisslot/p/12444967.html