溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

怎么在Angular2中使用Guard和Resolve實(shí)現(xiàn)權(quán)限控制

發(fā)布時(shí)間:2021-03-30 17:18:56 來(lái)源:億速云 閱讀:175 作者:Leah 欄目:web開(kāi)發(fā)

本篇文章為大家展示了怎么在Angular2中使用Guard和Resolve實(shí)現(xiàn)權(quán)限控制,內(nèi)容簡(jiǎn)明扼要并且容易理解,絕對(duì)能使你眼前一亮,通過(guò)這篇文章的詳細(xì)介紹希望你能有所收獲。

Guard

Guard 其實(shí)是一系列接口,只要你實(shí)現(xiàn)了它的方法,配置了這些 Guard ,Angular路由框架就會(huì)根據(jù)這個(gè)方法返回的 true 或 false 來(lái)判斷是否激活這個(gè)路由。它包括幾種類(lèi)型:

1、CanActivate

這種類(lèi)型的 Guard 用來(lái)控制是否允許進(jìn)入當(dāng)前的路徑。

2、CanActivateChild

這種類(lèi)型的 Guard 用來(lái)控制是否允許進(jìn)入當(dāng)前路徑的所有子路徑。

3、CanDeactivate

用來(lái)控制是否能離開(kāi)當(dāng)前頁(yè)面進(jìn)入別的路徑

4、CanLoad

用于控制一個(gè)異步加載的子模塊是否允許被加載。

以 CanActivate 為例,這個(gè)接口的定義如下:

exportinterface CanActivate {
 canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean;
}

這個(gè)接口定義了一個(gè)方法,當(dāng)你實(shí)現(xiàn)這個(gè)接口,并把它配置到某一個(gè)路由上以后,當(dāng)用戶進(jìn)入這個(gè)路由的路徑之前,就會(huì)調(diào)用它里面的 canActivate() 方法,它第一個(gè)參數(shù),就是將要激活的路由,第二個(gè)參數(shù)是路由器當(dāng)前的狀態(tài)。它返回一個(gè)布爾型的結(jié)果,或者是布爾型的 Promise Observable

Resolve

這跟Angular1中ui-router庫(kù)的 resolve 類(lèi)似,就是用來(lái)在打開(kāi)一個(gè)頁(yè)面之前先獲取數(shù)據(jù),而不是進(jìn)入頁(yè)面以后再加載。這個(gè)接口中的方法,可以返回任意的對(duì)象,也可以返回一個(gè) Promise ,或者 Observable

如果在一個(gè)路徑上同時(shí)設(shè)置了 CanActivate 和 Resolve ,首先 CanActivate 接口的方法會(huì)被執(zhí)行,當(dāng)這個(gè)路由可以被激活時(shí), Resolve 接口的方法才會(huì)被執(zhí)行。

實(shí)例

下面,我們來(lái)通過(guò)一個(gè)比較完整的實(shí)例,來(lái)看看, CanActivate , CanActivateChild , CanDeactivate 和 Resolve 的用法。( CanLoad 將會(huì)在之后介紹子模塊、異步加載的文章中再介紹)。這篇教程的源代碼可以在這里 查看。

場(chǎng)景

我們還是用之前的教程 Angular2入門(mén)教程-2 實(shí)現(xiàn)TodoList App 中的實(shí)例。

我們先來(lái)看一看要解決的一些問(wèn)題:

  1. 系統(tǒng)的默認(rèn)頁(yè)是home頁(yè)面,這個(gè)頁(yè)面不需要登錄也可以打開(kāi)。

  2. 登陸以后,管理員和用戶分別進(jìn)入不同的頁(yè)面。

  3. 所有的todo模塊的頁(yè)面都需要用戶角色,管理頁(yè)面需要管理員角色

  4. 在進(jìn)入任務(wù)列表頁(yè)面之前,需要獲取任務(wù)列表數(shù)據(jù),而不是進(jìn)入頁(yè)面以后再獲取數(shù)據(jù)。

  5. 當(dāng)用戶離開(kāi)任務(wù)詳情頁(yè)時(shí),提示是否確認(rèn)要離開(kāi)。

默認(rèn)頁(yè)面home

默認(rèn)頁(yè)面就是當(dāng)用戶直接打開(kāi)你的網(wǎng)頁(yè)域名,沒(méi)有輸入任何路徑的情況下,默認(rèn)打開(kāi)的頁(yè)面,在之前的教程已經(jīng)講過(guò),這是在配置路由的時(shí)候,用 redirect 實(shí)現(xiàn):

{
 path: '',
 redirectTo: '/home',
 pathMatch: 'full'
}

AuthService

首先我們需要一個(gè)權(quán)限驗(yàn)證的服務(wù) AuthService ,除了用來(lái)進(jìn)行登陸操作,還用于驗(yàn)證是否登陸,是否具有擁有某種角色。具體代碼如下:

import{ Observable }from'rxjs/Observable';
import'rxjs/add/observable/of';

@Injectable()
exportclassAuthService{
 account: Account;

// simulation to login.
 login(role: string): Observable<Account> {
letaccount =newAccount();
 account.id = 11;
 account.name = 'super man';
 account.roles = [role];
this.account = account;
returnObservable.of(account);
 }
 getAccount(): Account {
returnthis.account;
 }
 isLogdedin(): boolean {
returnthis.account &&this.account.id !=null;
 }
 hasRole(role: string): boolean {
returnthis.account &&this.account.roles.includes(role);
 }
}

在最上面我們注意到我們引入了 Observable 和它的一個(gè)方法 of 。這是由于我們的登陸操作一般都是去服務(wù)器端進(jìn)行登陸驗(yàn)證,而使用 Http 服務(wù)從服務(wù)器端獲取數(shù)據(jù)一般都是返回 Observable ,所以這里也使用 Observable 來(lái)返回登陸后的用戶信息。我們引入 of 方法,是因?yàn)槲覀儗?duì) Observable 的操作都是需要什么操作符就引入什么,而不是直接引入所有的。

最后的 hasRole(role) 方法的用途是,我們可以在頁(yè)面上通過(guò) ngIf="hasRole('CUSTOMER')" 的方式來(lái)控制是否顯示某個(gè)頁(yè)面元素。

原先的todo路由定義

之前todo模塊的路由是這樣:

exportconstTodoRoutes: Route[] = [
 {
 path: 'todo',
 children: [
 {
 path: 'list',
 component: TodoListComponent
 },
 {
 path: 'detail/:id',
 component: TodoDetailComponent
 }
 ]
 }
];

在路徑 todo 下面,有兩個(gè)子路由,分別是列表和詳情。

然后再針對(duì)下面的需求,一個(gè)個(gè)來(lái)解決:

  1. 所有的todo模塊的頁(yè)面都需要用戶角色

  2. 離開(kāi)詳情頁(yè)需要確認(rèn)

  3. 進(jìn)入列表頁(yè)面之前需要先獲取任務(wù)列表數(shù)據(jù)

控制所有todo模塊的都需要用戶角色

對(duì)于第一個(gè),我們要保護(hù)所有的todo模塊的頁(yè)面,也就是'/todo'路徑的所有子路徑,所以,我們可以使用 CanActivateChild 。這樣,在每進(jìn)入一個(gè)todo的子路徑的時(shí)候,都會(huì)先進(jìn)行檢查來(lái)判斷能否進(jìn)入。代碼如下:

import{ Injectable }from'@angular/core';
import{ CanActivateChild, Router,
 ActivatedRouteSnapshot, RouterStateSnapshot } from'@angular/router';

import{ AuthService }from'../services/auth.service';
import{ TodoDetailComponent }from'./detail/detail.component';

@Injectable()
exportclassMyTodoGuardimplementsCanActivateChild{

constructor(private authService: AuthService, private router: Router) {}

 canActivateChild(childRoute: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
if(!this.authService.isLogdedin()) {
 alert('You need to login!');
this.router.navigate(['/home']);
returnfalse;
 }
if(this.authService.hasRole('CUSTOMER')) {
returntrue;
 }
returnfalse;
 }
}

這個(gè) Guard 的實(shí)現(xiàn)很簡(jiǎn)單,就是用 authService 來(lái)判斷是否登陸,以及是否具有'CUSTOMER'角色。

注意這個(gè) Guard 的實(shí)現(xiàn)也必須是 Injectable 的,因?yàn)槲覀冃枰狝ngular的依賴注入幫我們創(chuàng)建實(shí)例和自動(dòng)注入。

離開(kāi)詳情頁(yè)需要確認(rèn)

接下來(lái)我們看怎么實(shí)現(xiàn)離開(kāi)詳情頁(yè)時(shí)的確認(rèn),也很簡(jiǎn)單,就是使用 CanDeactivate ,并把它定義在詳情頁(yè)的路由定義上。

@Injectable()
exportclassCanLeaveTodoDetailGuardimplementsCanDeactivate<TodoDetailComponent>{
 canDeactivate(component: TodoDetailComponent, route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
returnconfirm('Confirm?');
 }
}

為了簡(jiǎn)單,上面的方法直接調(diào)用 confirm('confirm?') 并返回它的結(jié)果,它會(huì)返回一個(gè)布爾型的結(jié)果,表示用戶是否確認(rèn)。如果用戶取消了,就不會(huì)離開(kāi)詳情頁(yè)。

進(jìn)入列表頁(yè)面之前需要先獲取數(shù)據(jù)

最后,再看看用 Resolve 來(lái)實(shí)現(xiàn)進(jìn)入一個(gè)頁(yè)面之前的數(shù)據(jù)初始化。

import{ Injectable }from'@angular/core';
import{ Resolve, ActivatedRouteSnapshot, RouterStateSnapshot }from'@angular/router';

import{ Todo }from'./todo';
import{ TodoService }from'./todo.service';

@Injectable()
exportclassMyTodoResolverimplementsResolve<Todo>{

constructor(private todoService: TodoService) { }

 resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
console.log('Get my todo list.');
returnthis.todoService.getAllTodos();
 }
}

在這個(gè) resolve() 方法中,直接返回調(diào)用 todoService getAllTodos() 方法的結(jié)果。對(duì)這個(gè) getAllTodos() 方法我們做一些修改,讓他返回一些測(cè)試數(shù)據(jù):

import{ Observable }from'rxjs/Observable';
import'rxjs/add/observable/of';
import'rxjs/add/operator/delay';

// 神略中間的部分
 getAllTodos(): Observable<Todo[]> {
lettodo1 =newTodo();
 todo1.id = 1;
 todo1.title = 'test task 1';
 todo1.createdDate = newDate();
 todo1.complete = false;
lettodo2 =newTodo();
 todo2.id = 2;
 todo2.title = 'test task 2';
 todo2.createdDate = newDate();
 todo2.complete = false;

this.todos = [todo1, todo2];
returnObservable.of(this.todos).delay(3000);
 }

在這個(gè)方法里我們創(chuàng)建了2個(gè)測(cè)試的任務(wù),封裝成 Observable 返回,并添加了一個(gè)3秒鐘的延時(shí),來(lái)模擬從服務(wù)器端獲取數(shù)據(jù)的過(guò)程。

通過(guò) Resolve 方式獲取的數(shù)據(jù),會(huì)放在被激活的當(dāng)前路由的 data 屬性里面,我們可以在組件中來(lái)獲得。所以,需要修改 TodoListComponent ,從路由的數(shù)據(jù) data 中獲取 todos 的值。然后就可以在頁(yè)面中顯示:

exportclassTodoListComponent{
 newTodo: Todo = newTodo();
 todos: Todo[];
constructor(private todoService: TodoService, private route: ActivatedRoute) {
this.todos =this.route.snapshot.data['todos'];
 }
// 省略其他
}

最終的todo模塊路由配置

最后我們?cè)倏纯醇由仙厦娴?Guard 和 Resolve 的路由配置以后,todo模塊的路由配置:

exportconstTodoRoutes: Route[] = [
 {
 path: 'todo',
 canActivateChild: [MyTodoGuard],
 children: [
 {
 path: 'list',
 component: TodoListComponent,
 resolve: { todos: MyTodoResolver }
 },
 {
 path: 'detail/:id',
 component: TodoDetailComponent,
 canDeactivate: [ CanLeaveTodoDetailGuard ]
 }
 ]
 }
];

我們?cè)?#39;todo'的路由上加了一個(gè) canActivateChild 控制能否激活子路徑, 在 list 的子路徑上配置了一個(gè) resolve 來(lái)獲取數(shù)據(jù),在 detail/:id 上配置了一個(gè) canDeactivate 來(lái)控制能否離開(kāi)。

最后,別忘了我們還需要在 todo 模塊的定義 TodoModule 里面的 providers 里添加這些,這樣依賴注入功能才能使用這些服務(wù)。

@NgModule({
 imports: [CommonModule, FormsModule ],
 declarations: [TodoListComponent, TodoDetailComponent, TodoItemComponent],
 providers: [TodoService, MyTodoResolver, MyTodoGuard, CanLeaveTodoDetailGuard]
})
exportclassTodoModule{}

通用的角色驗(yàn)證Guard

在上面的 MyTodoGuard 里面,我們判斷當(dāng)前的用戶是否具有 CUSTOMER 角色,如果我們能夠把這個(gè)需要判斷的 CUSTOMER 角色通過(guò)一種方式來(lái)傳遞到這個(gè)方法里面,然后通過(guò)傳遞不同的參數(shù),就可以用這個(gè)方法來(lái)判斷進(jìn)入任意頁(yè)面的用戶是否具有某個(gè)角色。我們可以使用Angular2路由里面的 data 屬性來(lái)實(shí)現(xiàn)。

當(dāng)我們定義一個(gè)路由時(shí),可以通過(guò) data 屬性來(lái)給這個(gè)路由添加一些數(shù)據(jù),如下:

exportconstTodoRoutes: Route[] = [
 {
 path: 'todo',
 data: {
 role: 'CUSTOMER'
 },
 canActivateChild: [MyTodoGuard],
 children: [
 {
 path: 'list',
 component: TodoListComponent,
 resolve: {
 todos: MyTodoResolver
 },
 data: {
 title: '列表'
 }
 },
 {
 path: 'detail/:id',
 component: TodoDetailComponent,
 canDeactivate: [ CanLeaveTodoDetailGuard ],
 data: {
 title: '詳情'
 }
 }
 ]
 }
];

我們給'todo'這個(gè)路由添加了1個(gè)變量,角色,我們可以在這個(gè)路由定義的組件以及它所有的子組件中的當(dāng)前路由中得到這些數(shù)據(jù)。而且在子路由里,都添加了一個(gè) title 的變量。然后在 TodoListComponent 里面就可以使用這個(gè)變量,比如在頁(yè)面上顯示。

exportclassTodoListComponent{
 newTodo: Todo = newTodo();
 todos: Todo[];
 title: string;

constructor(private todoService: TodoService, private route: ActivatedRoute) {
this.todos =this.route.snapshot.data['todos'];
this.title =this.route.data['title'];
 }
// 省略其他
}

我們可以通過(guò)這種方式,在每個(gè)路由上配置title屬性,然后就可以用一種通用的方式來(lái)實(shí)現(xiàn)在頁(yè)面上顯示面包屑導(dǎo)航欄的功能。

但是,在這個(gè)實(shí)例中,我們要用 data 上添加的 role: 'CUSTOMER' ,用它來(lái)表示當(dāng)前的這個(gè)路徑,需要有 CUSTOMER 角色的用戶才能訪問(wèn)。然后在 MyTodoGuard 里用它來(lái)判斷:

@Injectable()
exportclassMyTodoGuardimplementsCanActivateChild{

 canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
if(!this.authService.isLogdedin()) {
 alert('You need to login!');
this.router.navigate(['/home']);
returnfalse;
 }
letrequiredRole = next.data['role'];
if(requiredRole ==null||this.authService.hasRole(requiredRole)) {
returntrue;
 }
returnfalse;
 }
}

上述內(nèi)容就是怎么在Angular2中使用Guard和Resolve實(shí)現(xiàn)權(quán)限控制,你們學(xué)到知識(shí)或技能了嗎?如果還想學(xué)到更多技能或者豐富自己的知識(shí)儲(chǔ)備,歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI