- vừa được xem lúc

Blog#281: RBAC - Kiểm soát Truy cập Dựa trên Vai trò với Angular Templates (Song ngữ: VN - EN - JP)

0 0 16

Người đăng: NGUYỄN ANH TUẤN

Theo Viblo Asia

Hi các bạn, mình là TUẤN. Hiện đang là một Full-stack Web Developer tại Tokyo😊.

Nếu bạn thích bài viết, xin hãy cho mình 1 upvote và follow blog để ủng hộ mình có thêm động lực ra thêm nhiều bài viết hay hơn trong tương lại nhé.😊

Các bạn có đang triển khai Kiểm soát Truy cập Dựa trên Vai trò (Role Based Access Control - RBAC) trong Angular templates không? Một cách để làm điều này là sử dụng *ngIf, nhưng mình không chọn cách đó đâu, vì nó sẽ bao gồm các hàm tùy chỉnh trong template của Angular và sau này rất khó bảo trì. Cách đúng để làm là sử dụng các directive trong Angular 🚀.

RBAC là gì?

Kiểm soát truy cập dựa trên vai trò (Role-based access control - RBAC) đề cập đến ý tưởng gán quyền truy cập cho người dùng dựa trên vai trò của họ trong tổ chức. Nó mang lại một phương pháp đơn giản, dễ quản lý cho việc quản lý truy cập, tránh sai sót hơn so với việc gán quyền truy cập cho từng người dùng một cách riêng lẻ.

Triển khai

Giả sử chúng ta có một interface tài khoản với 3 thuộc tính: id, nameroles. Các roles là một mảng của enum types, đại diện cho các vai trò khác nhau mà tài khoản của chúng ta có thể có, bao gồm ADMIN, USER, EDITOR, VIEWER.

export interface IAccount { id: number; name: string; roles: ERoles[]
} export enum ERoles { admin = 'ADMIN', user = 'USER', editor = 'EDITOR', viewer = 'VIEWER'
} 

Thay vì sử dụng *ngIf, chúng ta muốn tạo một directive tùy chỉnh, chấp nhận các roles mà template sẽ được hiển thị và tài khoản mà các roles có thể được kiểm tra.

import { Directive, Input, OnChanges, TemplateRef, ViewContainerRef } from '@angular/core';
import { ERoles, IAccount } from '../../interfaces/account.interface'; @Directive({ selector: '[hasRoles]'
})
export class HasRolesDirective implements OnChanges { private visible: boolean; private roles: ERoles[]; private account: IAccount; @Input() set hasRoles(roles: ERoles[]) { this.roles = roles; } @Input('hasRolesFor') set hasRolesFor(account: IAccount) { this.account = account; }; constructor(private templateRef: TemplateRef<unknown>, private viewContainer: ViewContainerRef) {} ngOnChanges(): void { if (!this.roles?.length || !this.account) { return; } if (this.visible) { return; } // kiểm tra xem vai trò tài khoản có bao gồm ít nhất một trong các vai trò đã thiết lập không if (this.account.roles.some(role => this.roles.includes(role))) { this.viewContainer.clear(); this.viewContainer.createEmbeddedView(this.templateRef); this.visible = true; return; } this.viewContainer.clear(); this.visible = false; } }

Cách sử dụng?

Đầu tiên, trong file typescript của component, bạn cần có tài khoản và mảng roles (để đảm bảo kiểu dữ liệu an toàn), như ví dụ dưới đây:

account: IAccount = { id: 1, name: 'Klajdi', roles: [ERoles.editor],
}; roles: typeof ERoles = ERoles;

Sau đó, bạn có thể sử dụng nó trong thẻ HTML của bất kỳ template nào bạn muốn. Dưới đây là hai ví dụ:

<div *hasRoles="[roles.editor]; for: account">Editor only</div>
<div *hasRoles="[roles.user, roles.viewer]; for: account">User and viewer only</div>

Và đây là kết quả hiển thị trên trình duyệt:

Vì mình chỉ gán vai trò EDITOR cho tài khoản của mình, nên chỉ có thể thấy được div dành cho Editor 🚀🚀.

Directive này có thể mở rộng cho bất kỳ logic nào bạn muốn áp dụng. Bạn cũng có thể viết unit tests để đảm bảo rằng các new member không làm hỏng logic khi viết code mới.😀


English Version

Are you currently implementing Role-Based Access Control (RBAC) in Angular templates? One way to do this is by using *ngIf, but I don't recommend that approach because it would involve custom functions in the Angular template, making it difficult to maintain in the future. The correct way to do it is by using directives in Angular. 🚀

What is RBAC?

Role-Based Access Control (RBAC) refers to the idea of assigning access rights to users based on their roles within the organization. It provides a simple and manageable approach to access control, avoiding more errors compared to assigning access rights to individual users separately.

Implementation

Let's assume we have an account interface with three properties: id, name, and roles. Roles are an array of enum types, representing different roles that our account can have, including ADMIN, USER, EDITOR, and VIEWER.

export interface IAccount { id: number; name: string; roles: ERoles[];
} export enum ERoles { admin = 'ADMIN', user = 'USER', editor = 'EDITOR', viewer = 'VIEWER'
}

Instead of using *ngIf, we want to create a custom directive that accepts the roles for which the template will be displayed and the account against which the roles can be checked.

import { Directive, Input, OnChanges, TemplateRef, ViewContainerRef } from '@angular/core';
import { ERoles, IAccount } from '../../interfaces/account.interface'; @Directive({ selector: '[hasRoles]'
})
export class HasRolesDirective implements OnChanges { private visible: boolean; private roles: ERoles[]; private account: IAccount; @Input() set hasRoles(roles: ERoles[]) { this.roles = roles; } @Input('hasRolesFor') set hasRolesFor(account: IAccount) { this.account = account; }; constructor(private templateRef: TemplateRef<unknown>, private viewContainer: ViewContainerRef) {} ngOnChanges(): void { if (!this.roles?.length || !this.account) { return; } if (this.visible) { return; } // Check if the account's roles include at least one of the set roles if (this.account.roles.some(role => this.roles.includes(role))) { this.viewContainer.clear(); this.viewContainer.createEmbeddedView(this.templateRef); this.visible = true; return; } this.viewContainer.clear(); this.visible = false; } }

How to use it?

First, in the component's TypeScript file, you need to have an account and an array of roles (to ensure type safety), as shown in the example below:

account: IAccount = { id: 1, name: 'Klajdi', roles: [ERoles.editor],
}; roles: typeof ERoles = ERoles;

Then, you can use it in the HTML tag of any template you want. Here are two examples:

<div *hasRoles="[roles.editor]; hasRolesFor: account">Editor only</div>
<div *hasRoles="[roles.user, roles.viewer]; hasRolesFor: account">User and viewer only</div>

And here's the result displayed in the browser:

Since I only assigned the EDITOR role to my account, I can only see the div intended for Editors. 🚀🚀

This directive can be extended for any logic you want to apply. You can also write unit tests to ensure that new members don't break the logic when writing new code. 😀


日本語版

ベースのアクセス制御(RBAC)をAngularテンプレートで実装していますか?一つの方法は*ngIfを使用することですが、将来のメンテナンスが困難になる可能性があるため、このアプローチはお勧めしません。正しい方法は、Angularでディレクティブを使用することです。🚀

RBACとは?

ロールベースのアクセス制御(RBAC)とは、組織内のユーザーに対して役割に基づいてアクセス権限を割り当てる考え方を指します。これは、アクセス制御において個々のユーザーにアクセス権限を個別に割り当てるよりも、シンプルで管理しやすいアプローチを提供し、エラーの発生を回避します。

実装方法

まず、idnamerolesの3つのプロパティを持つアカウントインターフェースがあると仮定しましょう。Rolesは、ADMINUSEREDITORVIEWERを含む、アカウントが持つさまざまな役割を表す列挙型の配列です。

export interface IAccount { id: number; name: string; roles: ERoles[];
} export enum ERoles { admin = 'ADMIN', user = 'USER', editor = 'EDITOR', viewer = 'VIEWER'
}

*ngIfの代わりに、テンプレートを表示するための役割と役割をチェックするアカウントを受け入れるカスタムディレクティブを作成したいと思います。

import { Directive, Input, OnChanges, TemplateRef, ViewContainerRef } from '@angular/core';
import { ERoles, IAccount } from '../../interfaces/account.interface'; @Directive({ selector: '[hasRoles]'
})
export class HasRolesDirective implements OnChanges { private visible: boolean; private roles: ERoles[]; private account: IAccount; @Input() set hasRoles(roles: ERoles[]) { this.roles = roles; } @Input('hasRolesFor') set hasRolesFor(account: IAccount) { this.account = account; }; constructor(private templateRef: TemplateRef<unknown>, private viewContainer: ViewContainerRef) {} ngOnChanges(): void { if (!this.roles?.length || !this.account) { return; } if (this.visible) { return; } // アカウントのロールがセットされたロールのいずれかを含んでいるかをチェックします if (this.account.roles.some(role => this.roles.includes(role))) { this.viewContainer.clear(); this.viewContainer.createEmbeddedView(this.templateRef); this.visible = true; return; } this.viewContainer.clear(); this.visible = false; } }

使い方

まず、コンポーネントのTypeScriptファイルで、以下の例のようにアカウントと役割の配列(Typeの安全性を確保するため)を持つ必要があります。

account: IAccount = { id: 1, name: 'Klajdi', roles: [ERoles.editor],
}; roles: typeof ERoles = ERoles;

次に、任意のテンプレートのHTMLタグ内でそれを使用することができます。以下に2つの例を示します。

<div *hasRoles="[roles.editor]; hasRolesFor: account">Editor only</div>
<div *hasRoles="[roles.user, roles.viewer]; hasRolesFor: account">User and viewer only</div>

そして、ブラウザで表示される結果は次のとおりです。

私のアカウントにはEDITORの役割しか割り当てられていないため、エディター専用のdivしか表示されません。🚀🚀

このディレクティブは、適用する任意のロジックに拡張することができます。また、新しいコードを書く際に新しいメンバーがロジックを壊さないようにするために、ユニットテストを書くこともできます。😀

Cuối cùng

Như thường lệ, mình hy vọng bạn thích bài viết này và biết thêm được điều gì đó mới.

Nếu bạn thích bài viết, xin hãy cho mình 1 upvote và đăng ký để ủng hộ mình có thêm động lực ra thêm nhiều bài viết hay hơn trong tương lại nhé.

Cảm ơn và hẹn gặp bạn trong những bài viết tiếp theo. Thank you. 😊


Ae nào có dự định trở thành Dev hoặc BrSE tại Nhật (N2-N3, 2-3 năm exp trở lên hoặc zero tech có tiếng N1-N2, cả 2 đầu Nhật và VN) cần mình đưa roadmap hoặc review CV, hiểu hơn về các câu hỏi thường gặp khi interview Dev hoặc BrSE, cách deal lương cao... cần support thì cứ liên hệ mình qua zalo nhé: 0379302361 hoặc Facebook của mình. Hoặc có bất kỳ vấn đề về kỹ thuật nào cần hỏi thì cứ liên hệ mình nhé.

Bình luận

Bài viết tương tự

- vừa được xem lúc

Route in Angular JS

Router là một module được đặt tại @angular/router, cung cấp cho ứng dụng Angluar của chúng ta khả năng điều hướng và hiển thị nội dung phù hợp với địa chỉ URL. Với các ứng dụng web thông thường, việc điều hướng theo URL thường sẽ do phía server đảm nhiệm, giống như hình ảnh dưới đây là ví dụ với Rai

0 0 88

- vừa được xem lúc

Có gì mới trong Angular 11

Phiên bản Angular 11.0.0 được phát hành vào tháng 11 năm 2020. Bản phát hành chính Angular 11 cung cấp các bản cập nhật trên toàn bộ nền tảng, bao gồm CLI và các components.

0 0 91

- vừa được xem lúc

Pipe trong Angular

Xin chào tất cả mọi người, bài viết này minh xin giới thiệu về Pipe trong Angular, rất mong được mọi người theo dõi. 1) Pipe trong Angular là gì. . .

0 0 115

- vừa được xem lúc

Angular Reactive Forms: Cách sử dụng FormArray

Trong bài viết này chúng ta sẽ tìm hiểu khi nào và cách sử dụng FormArray, cả trong Component và trong Template, ngoài ra chúng ta sẽ xem cách để custom validation cho nó. Vì sao chúng ta cần sử dụng FormArray.

0 0 95

- vừa được xem lúc

RxJS và Reactive Programming

1 - Stream. Streams are a sequence of values over time.

0 0 344

- vừa được xem lúc

Mapping class Java với Angular Typescript model – Chưa bao giờ dễ đến thế

Xin chào mọi người hôm nay mình giới thiệu một loại đồ chơi cực xịn cực hay ho luôn, đây là một thư viện giúp cho mọi người tạo ra 1 class Typescript trong dự án Frontend ở đây mình lấy ví dụ là Angul

0 0 85