Blazor页面元素授权——AuthorizeView 组件的使用
上篇博客我们说到了blazor的身份认证的实现,对于AuthorizeView 组件来说,可以通过级联参数来获取包含了用户信息的AuthenticationState对象。
请注意,你需要引用Microsoft.AspNetCore.Components.Authorization Nuget包,并且在启动类中添加服务Services.AddAuthorizationCore();
使用CascadingAuthenticationState组件包裹App.razor中的代码:
<CascadingAuthenticationState>
<Router AppAssembly="@typeof(Program).Assembly">
<Found Context="routeData">
<AuthorizeRouteView RouteData="@routeData"
DefaultLayout="@typeof(MainLayout)" />
</Found>
<NotFound>
<LayoutView Layout="@typeof(MainLayout)">
<p>Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
</CascadingAuthenticationState>
如果未指定授权条件,则 AuthorizeView 使用默认策略:
- 将经过身份验证(已登录)的用户视为已授权。
- 将未经过身份验证(已注销)的用户视为未授权。
基于角色的授权方式
角色的获取方式是多样的,可以来自项目本身,也可以来自外部的权限系统。总之,你需要清楚角色对应的权限本身范围,比如控制了哪些页面可以访问,哪些按钮是可见的。
请记住,仅在客户端进行权限控制并不是安全的。
在AuthorizeView组件中包裹需要验证角色才可以查看的组件
<AuthorizeView Roles="admin">
<p>You can only see this if you're an admin or superuser.</p>
</AuthorizeView>
我个人是将认证与授权作为两个服务。授权服务根据系统码以及模块Id,调用权限管理系统接口,权限系统会根据当前用户来返回权限树,而我拿到权限树之后,会将其中的按钮或是页面的权限标识作为Roles,写入AuthenticationState当中,使用上面例子的方式,控制页面元素的展示。
下面附上完整的授权服务代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Security.Claims;
using System.Threading.Tasks;
using ClientSideTemplate.Client.Foundation.Authentication.Model;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Components.WebAssembly.Http;
using Newtonsoft.Json;
namespace ClientSideTemplate.Client.Foundation.Authentication
{
public class AuthorizedService
{
private readonly IServiceClient _serviceClient;
private readonly AuthorizedOption _authorizedOption;
public AuthorizedService(IServiceClient serviceClient, AuthorizedOption authorizedOption)
{
_serviceClient = serviceClient;
_authorizedOption = authorizedOption;
}
public async Task AuthorizedAsync(AuthenticationState state)
{
#if DEBUG
var identity = new ClaimsIdentity(_authorizedOption.ModuleIds.Select(x => new Claim(ClaimTypes.Role, x)));
state.User.AddIdentity(identity);
#else
foreach (var moduleId in _authorizedOption.ModuleIds)
{
var userInfo = await GetUserInfo(_authorizedOption.SystemCode, moduleId);
var identity = new ClaimsIdentity(userInfo.PrivilegeTree.Select(x => new Claim(ClaimTypes.Role, x.ModuleId)));
state.User.AddIdentity(identity);
}
#endif
}
private async Task<UserInfo> GetUserInfo(string systemCode, string moduleId)
{
var request = new HttpRequestMessage(HttpMethod.Get, $"/login/Auth/UserInfo?systemCode={systemCode}&moduleId={moduleId}");
request.SetBrowserRequestCredentials(BrowserRequestCredentials.Include);
var response = await _serviceClient.SendAsync(request);
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
#if !DEBUG
SignIn();
#endif
}
var content = await response.Content.ReadAsStringAsync();
if (string.IsNullOrWhiteSpace(content))
{
return new UserInfo();
}
return JsonConvert.DeserializeObject<ApiResult<UserInfo>>(content).Result;
}
}
}
另外,如果你想在代码中获取AuthenticationState,请以级联参数的形式:
[CascadingParameter]
private Task<AuthenticationState> authenticationStateTask { get; set; }