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, name và roles. 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)とは、組織内のユーザーに対して役割に基づいてアクセス権限を割り当てる考え方を指します。これは、アクセス制御において個々のユーザーにアクセス権限を個別に割り当てるよりも、シンプルで管理しやすいアプローチを提供し、エラーの発生を回避します。
実装方法
まず、id
、name
、roles
の3つのプロパティを持つアカウントインターフェースがあると仮定しましょう。Rolesは、ADMIN
、USER
、EDITOR
、VIEWER
を含む、アカウントが持つさまざまな役割を表す列挙型の配列です。
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ặcBrSE
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 interviewDev
hoặcBrSE
, 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é.