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; }