《进击吧!Blazor!》系列入门教程 第一章 6.安全
《进击吧!Blazor!》是本人与张善友老师合作的Blazor零基础入门教程视频,此教程能让一个从未接触过Blazor的程序员掌握开发Blazor应用的能力。
视频地址: https://space.bilibili.com/48...
Blazor WebAssembly 是单页应用 (SPA) 框架,用于使用 .NET 生成交互式客户端 Web 应用,采用 C# 代替 JavaScript 来编写前端代码
本系列文章因篇幅有限,省略了部分代码,完整示例代码: https://github.com/TimChen44/...
作者:陈超超
Ant Design Blazor 项目贡献者,拥有十多年从业经验,长期基于.Net 技术栈进行架构与开发产品的工作,现就职于正泰集团。
邮箱:[email protected]
欢迎各位读者有任何问题联系我,我们共同进步。
我的的 ToDo 应用基本功能已经完成,但是自己的待办当然只有自己知道,所以我们这次给我们的应用增加一些安全方面的功能。
Blazor 身份验证与授权
身份验证
Blazor Server 应用和 Blazor WebAssembly 应用的安全方案有所不同。
Blazor WebAssemblyBlazor WebAssembly 应用在客户端上运行。 由于用户可绕过客户端检查,因为用户可修改所有客户端代码, 因此授权仅用于确定要显示的 UI 选项,所有客户端应用程序技术都是如此。
Blazor ServerBlazor Server 应用通过使用 SignalR 创建的实时连接运行。 建立连接后,将处理基于 SignalR 的应用的身份验证。 可基于 cookie 或一些其他持有者令牌进行身份验证。
授权
AuthorizeView
组件根据用户是否获得授权来选择性地显示 UI 内容。 如果只需要为用户显示数据,而不需要在过程逻辑中使用用户的标识,那么此方法很有用。
<AuthorizeView>
<Authorized>
<!--验证通过显示-->
</Authorized>
<NotAuthorized>
<!--验证不通过显示-->
</NotAuthorized>
</AuthorizeView>
Blazor 中使用 Token
在 Blazor WebAssembly 模式下, 因为应用都在客户端运行,所以使用 Token 作为身份认证的方式是一个比较好的选择。
基本的使用时序图如下
对于安全要求不高的应用采用这个方法简单、易维护,完全没有问题。
但是 Token 本身在安全性上存在以下两个风险:
Token 无法注销,所以可以在 Token 有效期内发送的非法请求,服务端无能为力。 Token 通过 AES 加密存储在客户端,理论上可以进行离线破解,破解后就能任意伪造 Token。因此遇到安全要求非常高的应用时,我们需要认证服务进行 Token 的有效性验证
改造 ToDo
接着我们对之前的 ToDo 项目进行改造,让他支持登录功能。
ToDo.Shared
先把前后端交互所需的 Dto 创建了
public class LoginDto
{
public string UserName { get; set; }
public string Password { get; set; }
}
public class UserDto
{
public string Name { get; set; }
public string Token { get; set; }
}
ToDo.Server
先改造服务端,添加必要引用,编写身份认证代码等
添加引用
Microsoft.AspNetCore.Authentication.JwtBearerStartup.cs
添加 JwtBearer 配置
public void ConfigureServices(IServiceCollection services)
{
//......
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,//是否验证Issuer
ValidateAudience = true,//是否验证Audience
ValidateLifetime = true,//是否验证失效时间
ValidateIssuerSigningKey = true,//是否验证SecurityKey
ValidAudience = "guetClient",//Audience
ValidIssuer = "guetServer",//Issuer,这两项和签发jwt的设置一致
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("123456789012345678901234567890123456789"))//拿到SecurityKey
};
});
}
此处定义了 Token 的密钥,规则等,实际项目时可以将这些信息放到配置中。
AuthController.cs
行政验证控制器,用于验证用户身份,创建 Token 等。
[ApiController]
[Route("api/[controller]/[action]")]
public class AuthController : ControllerBase
{
//登录
[HttpPost]
public UserDto Login(LoginDto dto)
{
//模拟获得Token
var jwtToken = GetToken(dto.UserName);
return new() { Name = dto.UserName, Token = jwtToken };
}
//获得用户,当页面客户端页面刷新时调用以获得用户信息
[HttpGet]
public UserDto GetUser()
{
if (User.Identity.IsAuthenticated)//如果Token有效
{
var name = User.Claims.First(x => x.Type == ClaimTypes.Name).Value;//从Token中拿出用户ID
//模拟获得Token
var jwtToken = GetToken(name);
return new UserDto() { Name = name, Token = jwtToken };
}
else
{
return new UserDto() { Name = null, Token = null };
}
}
public string GetToken(string name)
{
//此处加入账号密码验证代码
var claims = new Claim[]
{
new Claim(ClaimTypes.Name,name),
new Claim(ClaimTypes.Role,"Admin"),
};
var key = new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes("123456789012345678901234567890123456789"));
var expires = DateTime.Now.AddDays(30);
var token = new JwtSecurityToken(
issuer: "guetServer",
audience: "guetClient",
claims: claims,
notBefore: DateTime.Now,
expires: expires,
signingCredentials: new SigningCredentials(key, SecurityAlgorithms.HmacSha256));
return new JwtSecurityTokenHandler().WriteToken(token);
}
}
ToDo.Client
改造客户端,让客户端支持身份认证
添加引用
Microsoft.AspNetCore.Components.AuthorizationAuthenticationStateProvider
AuthenticationStateProvider
是 AuthorizeView
组件和 CascadingAuthenticationState
组件用于获取身份验证状态的基础服务。
通常不直接使用 AuthenticationStateProvider
,直接使用主要缺点是,如果基础身份验证状态数据发生更改,不会自动通知组件。其次是项目中总会有一些自定义的认证逻辑。
所以我们通常写一个类继承他,并重写一些我们自己的逻辑。
//AuthProvider.cs
public class AuthProvider : AuthenticationStateProvider
{
private readonly HttpClient HttpClient;
public string UserName { get; set; }
public AuthProvider(HttpClient httpClient)
{
HttpClient = httpClient;
}
public async override Task<AuthenticationState> GetAuthenticationStateAsync()
{
//这里获得用户登录状态
var result = await HttpClient.GetFromJsonAsync<UserDto>($"api/Auth/GetUser");
if (result?.Name == null)
{
MarkUserAsLoggedOut();
return new AuthenticationState(new ClaimsPrincipal());
}
else
{
var claims = new List<Claim>();
claims.Add(new Claim(ClaimTypes.Name, result.Name));
var authenticatedUser = new ClaimsPrincipal(new ClaimsIdentity(claims, "apiauth"));
return new AuthenticationState(authenticatedUser);
}
}
/// <summary>
/// 标记授权
/// </summary>
/// <param name="loginModel"></param>
/// <returns></returns>
public void MarkUserAsAuthenticated(UserDto userDto)
{
HttpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", userDto.Token);
UserName = userDto.Name;
//此处应该根据服务器的返回的内容进行配置本地策略,作为演示,默认添加了“Admin”
var claims = new List<Claim>();
claims.Add(new Claim(ClaimTypes.Name, userDto.Name));
claims.Add(new Claim("Admin", "Admin"));
var authenticatedUser = new ClaimsPrincipal(new ClaimsIdentity(claims, "apiauth"));
var authState = Task.FromResult(new AuthenticationState(authenticatedUser));
NotifyAuthenticationStateChanged(authState);
//慈湖可以可以将Token存储在本地存储中,实现页面刷新无需登录
}
/// <summary>
/// 标记注销
/// </summary>
public void MarkUserAsLoggedOut()
{
HttpClient.DefaultRequestHeaders.Authorization = null;
UserName = null;
var anonymousUser = new ClaimsPrincipal(new ClaimsIdentity());
var authState = Task.FromResult(new AuthenticationState(anonymousUser));
NotifyAuthenticationStateChanged(authState);
}
}
NotifyAuthenticationStateChanged
方法会通知身份验证状态数据(例如 AuthorizeView)使用者使用新数据重新呈现。HttpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", userDto.Token);
将 HTTP 请求头中加入 Token,这样之后所有的请求都会带上 Token。
在Program
中注入AuthProvider
服务,以便于其他地方使用
//Program.cs
builder.Services.AddScoped<AuthenticationStateProvider, AuthProvider>();
在Program
中配置支持的策略
builder.Services.AddAuthorizationCore(option =>
{
option.AddPolicy("Admin", policy => policy.RequireClaim("Admin"));
});
登录界面
添加Login.razor
组件,代码如下
<div style="margin:100px">
<Spin Spinning="isLoading">
@if (model != null) {
<form
OnFinish="OnSave"
Model="@model"
LabelCol="new ColLayoutParam() {Span = 6 }"
>
<FormItem Label="用户名">
<input @bind-Value="context.UserName" />
</FormItem>
<FormItem Label="密码">
<input @bind-Value="context.Password" type="password" />
</FormItem>
<FormItem WrapperColOffset="6">
<button type="@ButtonType.Primary" HtmlType="submit">登录</button>
</FormItem>
</form>
}
</Spin>
</div>
public partial class Login
{
[Inject] public HttpClient Http { get; set; }
[Inject] public MessageService MsgSvr { get; set; }
[Inject] public AuthenticationStateProvider AuthProvider { get; set; }
LoginDto model = new LoginDto();
bool isLoading;
async void OnLogin()
{
isLoading = true;
var httpResponse = await Http.PostAsJsonAsync<LoginDto>($"api/Auth/Login", model);
UserDto result = await httpResponse.Content.ReadFromJsonAsync<UserDto>();
if (string.IsNullOrWhiteSpace(result?.Token) == false )
{
MsgSvr.Success($"登录成功");
((AuthProvider)AuthProvider).MarkUserAsAuthenticated(result);
}
else
{
MsgSvr.Error($"用户名或密码错误");
}
isLoading = false;
InvokeAsync( StateHasChanged);
}
}
登录界面代码很简单,就是向api/Auth/Login
请求,根据返回的结果判断是否登入成功。((AuthProvider)AuthProvider).MarkUserAsAuthenticated(result);
标记身份认证状态已经修改。
修改布局
修改MainLayout.razor
文件
<CascadingAuthenticationState>
<AuthorizeView>
<Authorized>
<Layout>
<Sider Style="overflow: auto;height: 100vh;position: fixed;left: 0;">
<div class="logo">进击吧!Blazor!</div>
<menu Theme="MenuTheme.Dark" Mode="@MenuMode.Inline">
<menuitem RouterLink="/"> 主页 </menuitem>
<menuitem RouterLink="/today" RouterMatch="NavLinkMatch.Prefix">
我的一天
</menuitem>
<menuitem RouterLink="/star" RouterMatch="NavLinkMatch.Prefix">
重要任务
</menuitem>
<menuitem RouterLink="/search" RouterMatch="NavLinkMatch.Prefix">
全部
</menuitem>
</menu>
</Sider>
<Layout Class="site-layout"> @Body </Layout>
</Layout>
</Authorized>
<NotAuthorized>
<ToDo.Client.Pages.Login></ToDo.Client.Pages.Login>
</NotAuthorized>
</AuthorizeView>
</CascadingAuthenticationState>
当授权通过后显示<AuthorizeView>
中<Authorized>
的菜单及主页,反之显示<NotAuthorized>
的Login
组件内容。
当需要根据权限显示不同内容,可以使用<AuthorizeView>
的Policy
属性实现,具体是在AuthenticationStateProvider
中通过配置策略,比如示例中claims.Add(new Claim("Admin", "Admin"));
就添加了Admin
策略,在页面上只需<AuthorizeView Policy="Admin">
就可以控制只有Admin
策略的账户显示其内容了。CascadingAuthenticationState
级联身份状态,它采用了 Balzor 组件中级联机制,这样我们可以在任意层级的组件中使用AuthorizeView
来控制 UI 了AuthorizeView
组件根据用户是否获得授权来选择性地显示 UI 内容。Authorized
组件中的内容只有在获得授权时显示。NotAuthorized
组件中的内容只有在未经授权时显示。
修改_Imports.razor
文件,添加必要的引用
@using Microsoft.AspNetCore.Components.Authorization
运行查看效果
更多关于安全
安全是一个很大的话题,这个章节只是介绍了其最简单的实现方式,还有更多内容推荐阅读官方文档:
https://docs.microsoft.com/zh...
次回预告
我们通过几张图表,将我们 ToDo 应用中任务情况做个完美统计。
学习资料
更多关于Blazor学习资料:https://aka.ms/LearnBlazor
社区文章
MicrosoftReactor
微软 Reactor 上海 是微软为构建开发者社区而提供的一个社区空间,以“予力多元化社区建设,帮助每一个开发者成就不凡”为使命,旨在通过不定期举办的技术讲座、开发者交流会面及技术沙龙和专题活动,帮助开发者和初创企业了解最新技术、学习最新知识、体验最新方案、结识业界同行、扩展职场人脉。
MicrosoftReactor
微软 Reactor 上海 是微软为构建开发者社区而提供的一个社区空间,以“予力多元化社区建设,帮助每一个开发者成就不凡”为使命,旨在通过不定期举办的技术讲座、开发者交流会面及技术沙龙和专题活动,帮助开发者和初创企业了解最新技术、学习最新知识、体验最新方案、结识业界同行、扩展职场人脉。
宣传栏
目录
《进击吧!Blazor!》是本人与张善友老师合作的Blazor零基础入门教程视频,此教程能让一个从未接触过Blazor的程序员掌握开发Blazor应用的能力。
视频地址: https://space.bilibili.com/48...
Blazor WebAssembly 是单页应用 (SPA) 框架,用于使用 .NET 生成交互式客户端 Web 应用,采用 C# 代替 JavaScript 来编写前端代码
本系列文章因篇幅有限,省略了部分代码,完整示例代码: https://github.com/TimChen44/...
作者:陈超超
Ant Design Blazor 项目贡献者,拥有十多年从业经验,长期基于.Net 技术栈进行架构与开发产品的工作,现就职于正泰集团。
邮箱:[email protected]
欢迎各位读者有任何问题联系我,我们共同进步。
我的的 ToDo 应用基本功能已经完成,但是自己的待办当然只有自己知道,所以我们这次给我们的应用增加一些安全方面的功能。
Blazor 身份验证与授权
身份验证
Blazor Server 应用和 Blazor WebAssembly 应用的安全方案有所不同。
Blazor WebAssemblyBlazor WebAssembly 应用在客户端上运行。 由于用户可绕过客户端检查,因为用户可修改所有客户端代码, 因此授权仅用于确定要显示的 UI 选项,所有客户端应用程序技术都是如此。
Blazor ServerBlazor Server 应用通过使用 SignalR 创建的实时连接运行。 建立连接后,将处理基于 SignalR 的应用的身份验证。 可基于 cookie 或一些其他持有者令牌进行身份验证。
授权
AuthorizeView
组件根据用户是否获得授权来选择性地显示 UI 内容。 如果只需要为用户显示数据,而不需要在过程逻辑中使用用户的标识,那么此方法很有用。
<AuthorizeView>
<Authorized>
<!--验证通过显示-->
</Authorized>
<NotAuthorized>
<!--验证不通过显示-->
</NotAuthorized>
</AuthorizeView>
Blazor 中使用 Token
在 Blazor WebAssembly 模式下, 因为应用都在客户端运行,所以使用 Token 作为身份认证的方式是一个比较好的选择。
基本的使用时序图如下
对于安全要求不高的应用采用这个方法简单、易维护,完全没有问题。
但是 Token 本身在安全性上存在以下两个风险:
Token 无法注销,所以可以在 Token 有效期内发送的非法请求,服务端无能为力。 Token 通过 AES 加密存储在客户端,理论上可以进行离线破解,破解后就能任意伪造 Token。因此遇到安全要求非常高的应用时,我们需要认证服务进行 Token 的有效性验证
改造 ToDo
接着我们对之前的 ToDo 项目进行改造,让他支持登录功能。
ToDo.Shared
先把前后端交互所需的 Dto 创建了
public class LoginDto
{
public string UserName { get; set; }
public string Password { get; set; }
}
public class UserDto
{
public string Name { get; set; }
public string Token { get; set; }
}
ToDo.Server
先改造服务端,添加必要引用,编写身份认证代码等
添加引用
Microsoft.AspNetCore.Authentication.JwtBearerStartup.cs
添加 JwtBearer 配置
public void ConfigureServices(IServiceCollection services)
{
//......
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,//是否验证Issuer
ValidateAudience = true,//是否验证Audience
ValidateLifetime = true,//是否验证失效时间
ValidateIssuerSigningKey = true,//是否验证SecurityKey
ValidAudience = "guetClient",//Audience
ValidIssuer = "guetServer",//Issuer,这两项和签发jwt的设置一致
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("123456789012345678901234567890123456789"))//拿到SecurityKey
};
});
}
此处定义了 Token 的密钥,规则等,实际项目时可以将这些信息放到配置中。
AuthController.cs
行政验证控制器,用于验证用户身份,创建 Token 等。
[ApiController]
[Route("api/[controller]/[action]")]
public class AuthController : ControllerBase
{
//登录
[HttpPost]
public UserDto Login(LoginDto dto)
{
//模拟获得Token
var jwtToken = GetToken(dto.UserName);
return new() { Name = dto.UserName, Token = jwtToken };
}
//获得用户,当页面客户端页面刷新时调用以获得用户信息
[HttpGet]
public UserDto GetUser()
{
if (User.Identity.IsAuthenticated)//如果Token有效
{
var name = User.Claims.First(x => x.Type == ClaimTypes.Name).Value;//从Token中拿出用户ID
//模拟获得Token
var jwtToken = GetToken(name);
return new UserDto() { Name = name, Token = jwtToken };
}
else
{
return new UserDto() { Name = null, Token = null };
}
}
public string GetToken(string name)
{
//此处加入账号密码验证代码
var claims = new Claim[]
{
new Claim(ClaimTypes.Name,name),
new Claim(ClaimTypes.Role,"Admin"),
};
var key = new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes("123456789012345678901234567890123456789"));
var expires = DateTime.Now.AddDays(30);
var token = new JwtSecurityToken(
issuer: "guetServer",
audience: "guetClient",
claims: claims,
notBefore: DateTime.Now,
expires: expires,
signingCredentials: new SigningCredentials(key, SecurityAlgorithms.HmacSha256));
return new JwtSecurityTokenHandler().WriteToken(token);
}
}
ToDo.Client
改造客户端,让客户端支持身份认证
添加引用
Microsoft.AspNetCore.Components.AuthorizationAuthenticationStateProvider
AuthenticationStateProvider
是 AuthorizeView
组件和 CascadingAuthenticationState
组件用于获取身份验证状态的基础服务。
通常不直接使用 AuthenticationStateProvider
,直接使用主要缺点是,如果基础身份验证状态数据发生更改,不会自动通知组件。其次是项目中总会有一些自定义的认证逻辑。
所以我们通常写一个类继承他,并重写一些我们自己的逻辑。
//AuthProvider.cs
public class AuthProvider : AuthenticationStateProvider
{
private readonly HttpClient HttpClient;
public string UserName { get; set; }
public AuthProvider(HttpClient httpClient)
{
HttpClient = httpClient;
}
public async override Task<AuthenticationState> GetAuthenticationStateAsync()
{
//这里获得用户登录状态
var result = await HttpClient.GetFromJsonAsync<UserDto>($"api/Auth/GetUser");
if (result?.Name == null)
{
MarkUserAsLoggedOut();
return new AuthenticationState(new ClaimsPrincipal());
}
else
{
var claims = new List<Claim>();
claims.Add(new Claim(ClaimTypes.Name, result.Name));
var authenticatedUser = new ClaimsPrincipal(new ClaimsIdentity(claims, "apiauth"));
return new AuthenticationState(authenticatedUser);
}
}
/// <summary>
/// 标记授权
/// </summary>
/// <param name="loginModel"></param>
/// <returns></returns>
public void MarkUserAsAuthenticated(UserDto userDto)
{
HttpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", userDto.Token);
UserName = userDto.Name;
//此处应该根据服务器的返回的内容进行配置本地策略,作为演示,默认添加了“Admin”
var claims = new List<Claim>();
claims.Add(new Claim(ClaimTypes.Name, userDto.Name));
claims.Add(new Claim("Admin", "Admin"));
var authenticatedUser = new ClaimsPrincipal(new ClaimsIdentity(claims, "apiauth"));
var authState = Task.FromResult(new AuthenticationState(authenticatedUser));
NotifyAuthenticationStateChanged(authState);
//慈湖可以可以将Token存储在本地存储中,实现页面刷新无需登录
}
/// <summary>
/// 标记注销
/// </summary>
public void MarkUserAsLoggedOut()
{
HttpClient.DefaultRequestHeaders.Authorization = null;
UserName = null;
var anonymousUser = new ClaimsPrincipal(new ClaimsIdentity());
var authState = Task.FromResult(new AuthenticationState(anonymousUser));
NotifyAuthenticationStateChanged(authState);
}
}
NotifyAuthenticationStateChanged
方法会通知身份验证状态数据(例如 AuthorizeView)使用者使用新数据重新呈现。HttpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", userDto.Token);
将 HTTP 请求头中加入 Token,这样之后所有的请求都会带上 Token。
在Program
中注入AuthProvider
服务,以便于其他地方使用
//Program.cs
builder.Services.AddScoped<AuthenticationStateProvider, AuthProvider>();
在Program
中配置支持的策略
builder.Services.AddAuthorizationCore(option =>
{
option.AddPolicy("Admin", policy => policy.RequireClaim("Admin"));
});
登录界面
添加Login.razor
组件,代码如下
<div style="margin:100px">
<Spin Spinning="isLoading">
@if (model != null) {
<form
OnFinish="OnSave"
Model="@model"
LabelCol="new ColLayoutParam() {Span = 6 }"
>
<FormItem Label="用户名">
<input @bind-Value="context.UserName" />
</FormItem>
<FormItem Label="密码">
<input @bind-Value="context.Password" type="password" />
</FormItem>
<FormItem WrapperColOffset="6">
<button type="@ButtonType.Primary" HtmlType="submit">登录</button>
</FormItem>
</form>
}
</Spin>
</div>
public partial class Login
{
[Inject] public HttpClient Http { get; set; }
[Inject] public MessageService MsgSvr { get; set; }
[Inject] public AuthenticationStateProvider AuthProvider { get; set; }
LoginDto model = new LoginDto();
bool isLoading;
async void OnLogin()
{
isLoading = true;
var httpResponse = await Http.PostAsJsonAsync<LoginDto>($"api/Auth/Login", model);
UserDto result = await httpResponse.Content.ReadFromJsonAsync<UserDto>();
if (string.IsNullOrWhiteSpace(result?.Token) == false )
{
MsgSvr.Success($"登录成功");
((AuthProvider)AuthProvider).MarkUserAsAuthenticated(result);
}
else
{
MsgSvr.Error($"用户名或密码错误");
}
isLoading = false;
InvokeAsync( StateHasChanged);
}
}
登录界面代码很简单,就是向api/Auth/Login
请求,根据返回的结果判断是否登入成功。((AuthProvider)AuthProvider).MarkUserAsAuthenticated(result);
标记身份认证状态已经修改。
修改布局
修改MainLayout.razor
文件
<CascadingAuthenticationState>
<AuthorizeView>
<Authorized>
<Layout>
<Sider Style="overflow: auto;height: 100vh;position: fixed;left: 0;">
<div class="logo">进击吧!Blazor!</div>
<menu Theme="MenuTheme.Dark" Mode="@MenuMode.Inline">
<menuitem RouterLink="/"> 主页 </menuitem>
<menuitem RouterLink="/today" RouterMatch="NavLinkMatch.Prefix">
我的一天
</menuitem>
<menuitem RouterLink="/star" RouterMatch="NavLinkMatch.Prefix">
重要任务
</menuitem>
<menuitem RouterLink="/search" RouterMatch="NavLinkMatch.Prefix">
全部
</menuitem>
</menu>
</Sider>
<Layout Class="site-layout"> @Body </Layout>
</Layout>
</Authorized>
<NotAuthorized>
<ToDo.Client.Pages.Login></ToDo.Client.Pages.Login>
</NotAuthorized>
</AuthorizeView>
</CascadingAuthenticationState>
当授权通过后显示<AuthorizeView>
中<Authorized>
的菜单及主页,反之显示<NotAuthorized>
的Login
组件内容。
当需要根据权限显示不同内容,可以使用<AuthorizeView>
的Policy
属性实现,具体是在AuthenticationStateProvider
中通过配置策略,比如示例中claims.Add(new Claim("Admin", "Admin"));
就添加了Admin
策略,在页面上只需<AuthorizeView Policy="Admin">
就可以控制只有Admin
策略的账户显示其内容了。CascadingAuthenticationState
级联身份状态,它采用了 Balzor 组件中级联机制,这样我们可以在任意层级的组件中使用AuthorizeView
来控制 UI 了AuthorizeView
组件根据用户是否获得授权来选择性地显示 UI 内容。Authorized
组件中的内容只有在获得授权时显示。NotAuthorized
组件中的内容只有在未经授权时显示。
修改_Imports.razor
文件,添加必要的引用
@using Microsoft.AspNetCore.Components.Authorization
运行查看效果
更多关于安全
安全是一个很大的话题,这个章节只是介绍了其最简单的实现方式,还有更多内容推荐阅读官方文档:
https://docs.microsoft.com/zh...
次回预告
我们通过几张图表,将我们 ToDo 应用中任务情况做个完美统计。
学习资料
更多关于Blazor学习资料:https://aka.ms/LearnBlazor