(26/30)大家一起學Blazor:給用戶分配角色

(26/30)大家一起學Blazor:給用戶分配角色

昨天角色的`CRUD` 功能都完成了,接著就是要把角色分配給用戶了

最後更新 2021/12/24 下午11:20
StrayaWorker
預計閱讀 5 分鐘
分類
Blazor
專題
一起學Blazor系列
標籤
.NET C# ASP.NET Core Blazor

昨天角色的CRUD 功能都完成了,接著就是要把角色分配給使用者,先建立一個 ViewModel CustomUserRoleViewModel,這是用來呈現角色底下使用者的 ViewModel。

namespace BlazorServer.ViewModels;

public class CustomUserRoleViewModel
{
	public string? UserId { get; set; }

	public string? UserName { get; set; }

	public bool IsSelected { get; set; }
}

再於IRolesRepositoryRolesRepository加上編輯角色內使用者的功能,這裡用了多載,第一個相當於Get 取得頁面初始資料,第二個則是Post 提交修改後的資料。

IRolesRepository.cs

…
    Task<List<CustomUserRoleViewModel>> EditUsersInRoleAsync(string roleId);
	Task<ResultViewModel> EditUsersInRoleAsync(List<CustomUserRoleViewModel> model, string roleId);

RolesRepository.cs

…
    public async Task<List<CustomUserRoleViewModel>> EditUsersInRoleAsync(string roleId)
	{
		var role = await _roleManager.FindByIdAsync(roleId);
		var model = new List<CustomUserRoleViewModel>();

        // 這裡注意,_userManager.Users.ToList()後面一定要加.ToList(),否則會拋出例外:https://stackoverflow.com/questions/60727080/helping-solving-there-is-already-an-open-datareader-associated-with-this-comman
		foreach (var user in _userManager.Users.ToList())
		{
			var userRoleViewModel = new CustomUserRoleViewModel
			{
				UserId = user.Id,
				UserName = user.UserName
			};

			if (await _userManager.IsInRoleAsync(user, role.Name))
				userRoleViewModel.IsSelected = true;
			else
				userRoleViewModel.IsSelected = false;
			model.Add(userRoleViewModel);
		}

		return model;
	}

	public async Task<ResultViewModel> EditUsersInRoleAsync(List<CustomUserRoleViewModel> model, string roleId)
	{
		var role = await _roleManager.FindByIdAsync(roleId);
		foreach (var m in model)
		{
			var user = await _userManager.FindByIdAsync(m.UserId);
			IdentityResult result;
			if (m.IsSelected && !await _userManager.IsInRoleAsync(user, role.Name))
				result = await _userManager.AddToRoleAsync(user, role.Name);
			else if (!m.IsSelected && await _userManager.IsInRoleAsync(user, role.Name))
				result = await _userManager.RemoveFromRoleAsync(user, role.Name);
			else
				continue;
			if (result.Succeeded)
			{
				if (model.Count > 0)
					continue;
				return new ResultViewModel
				{
					Message = roleId,
					IsSuccess = true
				};
			}

			return new ResultViewModel
			{
				Message = roleId,
				IsSuccess = false
			};
		}

		return new ResultViewModel
		{
			Message = roleId,
			IsSuccess = true
		};
	}

接著加上頁面。

EditUsersInRole.razor.cs

using BlazorServer.Repository;
using BlazorServer.Shared;
using BlazorServer.ViewModels;
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;

namespace BlazorServer.Pages.RolesManagement;

public partial class EditUsersInRole
{
	[Inject] protected IRolesRepository? RolesRepository { get; set; }
	[Inject] protected NavigationManager? NavigationManager { get; set; }
	[Inject] protected IJSRuntime? Js { get; set; }
	private JsInteropClasses? _jsClass;
	[Parameter] public string? Id { get; set; }
	public List<CustomUserRoleViewModel> UserRoleViewModel { get; set; } = new List<CustomUserRoleViewModel>();

	protected override async Task OnInitializedAsync()
	{
		await LoadData();
		_jsClass = new JsInteropClasses(Js!);
	}

	private async Task LoadData()
	{
		UserRoleViewModel = (await RolesRepository!.EditUsersInRoleAsync(Id!)).ToList();
	}


	public async Task HandleValidSubmit()
	{
		var result = await RolesRepository!.EditUsersInRoleAsync(UserRoleViewModel, Id!);

		if (result.IsSuccess)
		{
			NavigationManager!.NavigateTo($"/RolesManagement/EditRole/{Id}");
		}
		else
		{
			await _jsClass!.Alert(result.Message!);
		}
	}

	public void Cancel()
	{
		NavigationManager!.NavigateTo($"/RolesManagement/EditRole/{Id}");
	}
}

EditUsersInRole.razor

@page "/RolesManagement/EditUsersInRole/{Id}" @attribute [Authorize]

<EditForm Model="UserRoleViewModel" OnValidSubmit="HandleValidSubmit">
  <DataAnnotationsValidator />
  <ValidationSummary />
  <div class="card">
    <div class="card-header">
      <h2>從角色新增或刪除使用者</h2>
    </div>
    <div class="card-body">
      @foreach (var user in UserRoleViewModel) {
      <div class="form-check m-1">
        <label class="form-check-label">
          <InputCheckbox @bind-Value="@user.IsSelected"></InputCheckbox>
          @user.UserName
        </label>
      </div>
      }
    </div>
    <div class="card-footer">
      <button type="submit" class="btn btn-primary">更新</button>
      <button type="button" class="btn btn-danger" @onclick="@Cancel">
        取消
      </button>
    </div>
  </div>
</EditForm>

EditRole.razor.cs新增一個方法,跳轉EditUsersInRole.razor,可以編輯角色底下使用者。

public void EditUsersInRole()
{
    NavigationManager!.NavigateTo($"/RolesManagement/EditUsersInRole/{Id}");
}

EditRole.razor加上一個按鈕呼叫該方法。

<button type="button" class="btn btn-info" @onclick="EditUsersInRole">
  新增或移除該角色底下的使用者
</button>

啟動網頁進入編輯角色使用者頁面,可以看到目前只有一個使用者,將Admin角色分配給這個使用者,如此一來就完成了Admintest@gmail.com的分配,不過要測試是否成功的話,需要對頁面加入限制,還要新增一個使用者測試。

先將每個razor component@attribute [Authorize]改成@attribute [Authorize(Roles = "Admin")],表示要看到這頁面的人必須有 Role Admin

再去NavMenu.razor,於原本的<AuthorizeView>上方加入下面這段程式碼,同理要看到跳轉Roles連結的人必須有 Role Admin,再把原本<AuthorizeView>Roles連結刪除。

<AuthorizeView Roles="Admin">
  <Authorized>
    <li class="nav-item px-3">
      <NavLink
        class="nav-link"
        href="RolesManagement/RolesList"
        Match="NavLinkMatch.All"
      >
        <span class="bi bi-kanban-fill h4 p-2 mb-0" aria-hidden="true"></span>
        Roles
      </NavLink>
    </li>
  </Authorized>
</AuthorizeView>

最後新增一名使用者,名為user@gmail.com,以此使用者登入,會發現左邊的Roles不見了,手動輸入網址也會提示沒有權限,這就是最基本的角色(Role)授權,如果系統很簡單只要用角色劃分權限,這樣就能滿足需求了。

引用:

  1. Add or remove users from role in asp net core

註:本文程式碼透過 .NET 6 + Visual Studio 2022 重構,可點選原文連結與重構後程式碼比較學習,謝謝閱讀,支援原作者

繼續探索

延伸閱讀

更多文章
同分類 / 同標籤 2021/12/25

(29/30)大家一起學Blazor:Blazor單元測試

開發一個系統最無聊的過程大概就是解決 Bug 了,尤其是那種嘗試對 null 物件取值的錯誤(`Object reference not set to an instance of an object.`),這應該是大部分人剛踏入程式設計領域最常碰到的問題,為了從枯燥的解決 Bug 過程解脫,這篇就來介紹`單元測試`。

繼續閱讀
同分類 / 同標籤 2021/12/25

(28/30)大家一起學Blazor:Policy-based authorization

之前有說到`ASP.NET Core Identity` 使用的是基於`Claim` 的驗證,其實`ASP.NET Core Identity` 有不同類型的授權方式,最簡單的`登入授權`、`角色授權`、`Claim 授權`,但上述幾種都是以一種方式實現:原則授權(`Policy-based authorization`)。

繼續閱讀