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

xasdfg 2020-03-08

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

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

应用程序身份验证

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

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

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

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

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

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

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

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

创建应用程序

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

打开Visual Studio 2019。

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

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

添加Nuget软件包

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

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

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

添加对以下库的引用:

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

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

另外还有

  • Microsoft.AspNetCore.Blazor.HttpClient

添加Cookie身份验证

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

打开Startup.cs文件。

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

// ******
// BLAZOR COOKIE Auth Code (begin)
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Http;
using System.Net.Http;
// BLAZOR COOKIE Auth Code (end)
// ******

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

public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }
        public IConfiguration Configuration { get; }
        // This method gets called by the runtime. Use this method to 
        // add services to the container.
        // For more information on how to configure your application, 
        // visit https://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        {
            // ******
            // BLAZOR COOKIE Auth Code (begin)
            services.Configure<CookiePolicyOptions>(options =>
            {
                options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });
            services.AddAuthentication(
                CookieAuthenticationDefaults.AuthenticationScheme)
                .AddCookie();
            // BLAZOR COOKIE Auth Code (end)
            // ******
            services.AddRazorPages();
            services.AddServerSideBlazor();
            services.AddSingleton<WeatherForecastService>();
            // ******
            // BLAZOR COOKIE Auth Code (begin)
            // From: https://github.com/aspnet/Blazor/issues/1554
            // HttpContextAccessor
            services.AddHttpContextAccessor();
            services.AddScoped<HttpContextAccessor>();
            services.AddHttpClient();
            services.AddScoped<HttpClient>();
            // BLAZOR COOKIE Auth Code (end)
            // ******
        }
        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Error");
                // The default HSTS value is 30 days. 
                // You may want to change this for production scenarios, 
                // see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }
            app.UseHttpsRedirection();
            app.UseStaticFiles();
            app.UseRouting();
            // ******
            // BLAZOR COOKIE Auth Code (begin)
            app.UseHttpsRedirection();
            app.UseStaticFiles();
            app.UseCookiePolicy();
            app.UseAuthentication();
            // BLAZOR COOKIE Auth Code (end)
            // ******
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapBlazorHub();
                endpoints.MapFallbackToPage("/_Host");
            });
        }
    }

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

这段代码还添加了:

  • HttpContextAccessor
  • HttpClient

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

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

添加登录/注销页面

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

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

添加以下Razor页面和代码:

Login.cshtml

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

Login.cshtml.cs

using System;
using System.Collections.Generic;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace BlazorCookieAuth.Server.Pages
{
    [AllowAnonymous]
    public class LoginModel : PageModel
    {
        public string ReturnUrl { get; set; }
        public async Task<IActionResult> 
            OnGetAsync(string paramUsername, string paramPassword)
        {
            string returnUrl = Url.Content("~/");
            try
            {
                // 清除现有的外部Cookie
                await HttpContext
                    .SignOutAsync(
                    CookieAuthenticationDefaults.AuthenticationScheme);
            }
            catch { }
            // *** !!! 在这里您可以验证用户 !!! ***
            // 在此示例中,我们仅登录用户(此示例始终登录用户)
            //
            var claims = new List<Claim>
            {
                new Claim(ClaimTypes.Name, paramUsername),
                new Claim(ClaimTypes.Role, "Administrator"),
            };
            var claimsIdentity = new ClaimsIdentity(
                claims, CookieAuthenticationDefaults.AuthenticationScheme);
            var authProperties = new AuthenticationProperties
            {
                IsPersistent = true,
                RedirectUri = this.Request.Host.Value
            };
            try
            {
                await HttpContext.SignInAsync(
                CookieAuthenticationDefaults.AuthenticationScheme,
                new ClaimsPrincipal(claimsIdentity),
                authProperties);
            }
            catch (Exception ex)
            {
                string error = ex.Message;
            }
            return LocalRedirect(returnUrl);
        }
    }
}

Logout.cshtml

@page
@model BlazorCookieAuth.Server.Pages.LogoutModel
@{
    ViewData["Title"] = "Logout";
}
<h2>Logout</h2>

Logout.cshtml.cs

using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace BlazorCookieAuth.Server.Pages
{
    public class LogoutModel : PageModel
    {
        public async Task<IActionResult> OnGetAsync()
        {
            // 清除现有的外部Cookie
            await HttpContext
                .SignOutAsync(
                CookieAuthenticationDefaults.AuthenticationScheme);
            return LocalRedirect(Url.Content("~/"));
        }
    }
}

添加客户代码

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

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

@page "/loginControl"
@using System.Web;
<AuthorizeView>
    <Authorized>
        <b>Hello, @context.User.Identity.Name!</b>
        <a class="ml-md-auto btn btn-primary"
           href="/logout?returnUrl=/"
           target="_top">Logout</a>
    </Authorized>
    <NotAuthorized>
        <input type="text"
               placeholder="User Name"
               @bind="@Username" />
        &nbsp;&nbsp;
        <input type="password"
               placeholder="Password"
               @bind="@Password" />
        <a class="ml-md-auto btn btn-primary"
           href="/(@Username)&(@Password)"
           target="_top">Login</a>
    </NotAuthorized>
</AuthorizeView>
@code {
    string Username = "";
    string Password = "";
    private string encode(string param)
    {
        return HttpUtility.UrlEncode(param);
    }
}

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

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

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

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

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

@inherits LayoutComponentBase
<div class="sidebar">
    <NavMenu />
</div>
<div class="main">
    <div class="top-row px-4">
        <!-- BLAZOR COOKIE Auth Code (begin) -->
        <LoginControl />
        <!-- BLAZOR COOKIE Auth Code (end) -->
    </div>
    <div class="content px-4">
        @Body
    </div>
</div>

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

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

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

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

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

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

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

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

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

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

当我们单击注销...

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

Cookie被删除。

调用服务器端控制器方法

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

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

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

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

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

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

using Microsoft.AspNetCore.Mvc;
namespace BlazorCookieAuth.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class UserController : Controller
    {
        // /api/User/GetUser
        [HttpGet("[action]")]
        public UserModel GetUser()
        {
            // Instantiate a UserModel
            var userModel = new UserModel
            {
                UserName = "[]",
                IsAuthenticated = false
            };
            // Detect if the user is authenticated
            if (User.Identity.IsAuthenticated)
            {
                // Set the username of the authenticated user
                userModel.UserName = 
                    User.Identity.Name;
                userModel.IsAuthenticated = 
                    User.Identity.IsAuthenticated;
            };
            return userModel;
        }
    }
    // Class to hold the UserModel
    public class UserModel
    {
        public string UserName { get; set; }
        public bool IsAuthenticated { get; set; }
    }
}

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

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

@page "/CallServerSide"
@using BlazorCookieAuth.Controllers
@using System.Net.Http
@inject HttpClient Http
@inject NavigationManager UriHelper
@inject Microsoft.AspNetCore.Http.IHttpContextAccessor HttpContextAccessor
<h3>Call Server Side</h3>
<p>Current User: @CurrentUser.UserName</p>
<p>IsAuthenticated: @CurrentUser.IsAuthenticated</p>
<button class="btn btn-primary" @onclick="GetUser">Get User</button>
@code {
    UserModel CurrentUser = new UserModel();
    async Task GetUser()
    {
        // Call the server side controller
        var url = UriHelper.ToAbsoluteUri("/api/User/GetUser");
        var result = await Http.GetJsonAsync<UserModel>(url.ToString());
        // Update the result
        CurrentUser.UserName = result.UserName;
        CurrentUser.IsAuthenticated = result.IsAuthenticated;
    }
}

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

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

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

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

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

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

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

async Task GetUser()
    {
        // Code courtesy from Oqtane.org (@sbwalker)
        // We must pass the authentication cookie in server side requests
        var authToken =
        HttpContextAccessor.HttpContext.Request.Cookies[".AspNetCore.Cookies"];
        if (authToken != null)
        {
            Http.DefaultRequestHeaders
            .Add("Cookie", ".AspNetCore.Cookies=" + authToken);
            // Call the server side controller
            var url = UriHelper.ToAbsoluteUri("/api/User/GetUser");
            var result = await Http.GetJsonAsync<UserModel>(url.ToString());
            // Update the result
            CurrentUser.UserName = result.UserName;
            CurrentUser.IsAuthenticated = result.IsAuthenticated;
        }
    }

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

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

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

相关推荐