为了演示身份验证如何在服务器端 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语句:

 // ******
// 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 如何让我们确定登录用户是谁的完整解释。

添加登录/注销页面

登录(和注销)由.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("~/"));
}
}
}

添加客户代码

使用以下代码将一个名为 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="/login?paramUsername=@encode(@Username)&paramPassword=@encode(@Password)"
target="_top">Login</a>
</NotAuthorized>
</AuthorizeView>
@code {
string Username = "";
string Password = "";
private string encode(string param)
{
return HttpUtility.UrlEncode(param);
}
}

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

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

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

最后,我们将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应用程序中每个页面的顶部。

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

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

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

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

当我们单击注销...

Cookie被删除。

调用服务器端控制器方法

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

为了演示这一点,我们首先打开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)
// ******

接下来,我们创建一个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; }
}
}

我们使用以下代码添加一个新的.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>

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

我们导航到新的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身份验证的演示的更多相关文章

  1. asp.net core中使用cookie身份验证

    配置 在 Startup.ConfigureServices 方法中,创建具有 AddAuthentication 和 AddCookie 方法的身份验证中间件服务: services.AddAuth ...

  2. 讨论一下.NET里,对cookie身份验证的超时的处理

    引言 在.NET里提供了FormsAuthentication类用来对用户身份进行验证和授权.不过,对于cookie的超时处理,一直是一个头疼的问题.这里介绍一下微软对.NET 身份验证超时的处理机制 ...

  3. ASP.NET MVC Cookie 身份验证

    1 创建一个ASP.NET MVC 项目 添加一个 AccountController 类. public class AccountController : Controller { [HttpGe ...

  4. 使用 cookie 的身份验证和授权

    前言 在上一章 学学 dotnet core 中的身份验证和授权-1-概念 中,我们大致明白了身份验证和授权两者的关系.那么在本文中,我们将使用 cookie 来做一个简单的身份验证和授权. 本文中我 ...

  5. asp.net 简单的身份验证

    1 通常我们希望已经通过身份验证的才能够登录到网站的后台管理界面,对于asp.net 介绍一种简单的身份验证方式 首先在webconfig文件中添加如下的代码 <!--身份验证--> &l ...

  6. ASP.NET没有魔法——ASP.NET Identity 的“多重”身份验证代码篇

    上篇文章介绍了ASP.NET中身份验证的机制与流程,本文将使用代码的来介绍如何实现第三方账户验证与双因子验证. 本章主要内容有: ● 实现基于微软账户的第三方身份验证 ● 实现双因子身份验证 ● 验证 ...

  7. Forms身份验证 知识总结

    最简单的Forms验证实现方法:FormsAuthentication.SetAuthCookie()方法,传递一个登录名即可FormsAuthentication.SignOut()方法退出Form ...

  8. ASP.NET没有魔法——ASP.NET Identity 的“多重”身份验证

    ASP.NET Identity除了提供基于Cookie的身份验证外,还提供了一些高级功能,如多次输入错误账户信息后会锁定用户禁止登录.集成第三方验证.账户的二次验证等,并且ASP.NET MVC的默 ...

  9. asp.net core 3.x 身份验证-1涉及到的概念

    前言 从本篇开始将围绕asp.net core身份验证写个小系列,希望你看完本系列后,脑子里对asp.net core的身份验证原理有个大致印象.至于身份验证是啥?与授权有啥联系?就不介绍了,太啰嗦. ...

随机推荐

  1. LeetCode No.100,101,102

    No.100 IsSameTree 相同的树 题目 给定两个二叉树,编写一个函数来检验它们是否相同. 如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的. 示例 输入: 1 1 / \ ...

  2. day19-3个双下item方法

    #使用双下item方法来实现属性的增删改查: # 查:__getitem__ 增改:__setitem__ 删除: __delitem__ class Goods: def __init__(self ...

  3. ununtu 16.04 下的 VsCode 下载与安装

    Vscode发现用包下载显示找不到网页,于是只有继续折腾. 折腾如下: ubuntu-desktop You can update your system with unsupported packa ...

  4. IPC之——信号量集(多个信号量)

    如果两个进程不仅需要同步,还要保证先后执行顺序,就要用两个信号量(互斥锁)来解决 //栅栏模型:实现以下框架中的四个子进程 所有进程做完任务后 在一起执行下一次  #include <stdio ...

  5. COMET探索系列三【异步通知服务器关闭数据连接实现思路】

    在小编络络 COMET实践笔记一文中注意事项中有这么一段话 使用长连接时, 存在一个很常见的场景:客户端需要关闭页 面,而服务器端还处在读取数据的阻塞状态,客户端需要及时通知服务器端关闭数据连接.服务 ...

  6. springboot学习笔记:6.内置tomcat启动和外部tomcat部署总结

    springboot的web项目的启动主要分为: 一.使用内置tomcat启动 启动方式: 1.IDEA中main函数启动 2.mvn springboot-run 命令 3.java -jar XX ...

  7. 错误修改.bashrc文件导致所有命令无法使用解决方法

    export PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin

  8. iOS UITableView Tips(2)

    #TableView Tips(2) (本来想一章就结束TableView Tips,但是发现自己还是太天真了~too young,too simple) ##架构上的优化 在Tips(1)中指出了一 ...

  9. 从源码看commit和commitAllowingStateLoss方法区别

    Fragment介绍 在很久以前,也就是我刚开始写Android时(大约在2012年的冬天--),那时候如果要实现像下面微信一样的Tab切换页面,需要继承TabActivity,然后使用TabHost ...

  10. 吴裕雄--天生自然HTML学习笔记:HTML 基础- 4个实例

    HTML 标题 HTML 标题(Heading)是通过<h1> - <h6> 标签来定义的. 实例 <h1>这是一个标题</h1> <h2> ...