在本教程中,我们将了解如何基于用户角色使用AuthGuard进行Angular 10安全路由。可以基于令牌和AuthInterceptor for API来保护路由是可以的,但是我们也可以根据用户的角色来保护路由。
如果用户无权查看该页面,您可能已经看到很多显示错误的应用程序。然后,在我们的角度应用程序中创建它。
按用户角色的安全路由
现在,我们有一个提供当前登录用户角色的API。我们还在客户端维护的用户角色对象,因此我们可以在访问路由时进行匹配。
假设我们有4个用户角色
export enum Roles {
ADMIN = 'ADMIN',
BUYER = 'BUYER',
SELLER = 'SELLER',
DATA_MANAGER = 'DATA MANAGER'
}
现在,我们有了用户角色列表,我们可以将用户角色分配给有权访问该页面的个人路线。
让我们做些改变 app-routing.module.ts
const routes: Routes = [
{
path: 'onboard', loadChildren: () => import('../onboard/onboard.module').then(m => m.OnboardModule),
canActivate: [AuthGuard],
data: {
userRoles: [] // All User Can Access but must be login
}
},
{
path: 'dashboard', loadChildren: () => import('../dashboard/dashboard.module').then(m => m.DashboardModule),
canActivate: [AuthGuard],
data: {
userRoles: [Roles.ADMIN, Roles.BUYER, Roles.SELLER, Roles.DATA_MANAGER] // Multiple Allowed User
}
},
{
path: 'data-importer', loadChildren: () => import('../data-importer/data-importer.module').then(m => m.DataImporterModule),
canActivate: [AuthGuard],
data: {
userRoles: [Roles.DATA_MANAGER] // Single Allowed User
}
...
];
@NgModule({
imports: [RouterModule.forRoot(appRoutes)],
exports: [RouterModule]
})
export class AppRouterModule {}
现在,在这里我们已经设置了两件事来根据用户角色保护路由。
-
canActivate: [AuthGuard]
:
AuthGuard 将包含或逻辑来检查数据对象中分配的用户角色是否与当前登录的用户角色匹配。 -
data: { userRoles: [Roles.DATA_MANAGER, ...] }
:
将为相应的路由设置userRoles数组,稍后我们可以在AuthGuard类中访问该数据。每个路由都可以有其唯一的用户可以访问。
使用基于用户角色的AuthGuard保护路由
现在,我们进入authguard实现,其中将包含可以通过验证用户角色来访问路由的方法。
import { Injectable, Inject } from '@angular/core';
import { Router, CanActivate, CanActivateChild, CanLoad, ActivatedRouteSnapshot} from '@angular/router';
import { Roles } from './util/user-roles';
import { AuthorizationService } from './authorization.service';
@Injectable()
export class AuthGuard implements CanActivate, CanActivateChild, CanLoad {
constructor(
@Inject(AuthorizationService) private authorizationService: AuthorizationService,
@Inject(Router) private router: Router
) {}
/**
* Can this route be activated?
* @param {ActivatedRouteSnapshot} route - The route.
* @returns {Promise<boolean>} True if user is authenticated otherwise false
*/
public async canActivate(route: ActivatedRouteSnapshot): Promise<boolean> {
const allowedUserRoles = this.getRoutePermissions(route);
return await this.checkPermission(allowedUserRoles);
}
/**
* Can this child route be activated?
* @param {ActivatedRouteSnapshot} route - The route.
* @returns {Promise<boolean>} True if user is authenticated otherwise false
*/
public async canActivateChild(route: ActivatedRouteSnapshot): Promise<boolean> {
const allowedUserRoles = this.getRoutePermissions(route);
return await this.checkPermission(allowedUserRoles);
}
/**
* Can this route be loaded.
* @returns {Promise<boolean>} True if user is authenticated otherwise false
*/
public canLoad(): Promise<boolean> {
return this.checkPermission(null);
}
/**
* Get allowed user roles from the route.
* @param {ActivatedRouteSnapshot} route - The route.
* @returns {string[]} All user roles that are allowed to access the route.
*/
private getRoutePermissions(route: ActivatedRouteSnapshot): Roles[] {
if (route.data && route.data.userRoles) {
return route.data.userRoles as Roles[];
}
return null;
}
/**
* Check if a user is authenticated
* @param {string[]} allowedUserRoles - These user roles have the permissions to access the route.
* @returns {Promise<boolean>} True if user is authenticated otherwise false
*/
private checkPermission(allowedUserRoles: Roles[]): Promise<boolean> {
return this.authorizationService.getSession().then((session: boolean) => {
if (session) {
if (!allowedUserRoles) {
return true; // if no user roles has been set, all user are allowed to access the route
} else {
return this.authorizationService.getUserRoles().then((userRoles: string[]) => {
if (this.authorizationService.areUserRolesAllowed(userRoles, allowedUserRoles)) {
return true;
} else {
this.router.navigateByUrl('/nopermission');
return false;
}
});
}
} else { return false; }
});
}
}
的三个主要功能 角路由器 are CanActivate
, CanActivateChild
, CanLoad
. With these methods, we can manage our authentication for routes. These methods will have a return type boolean i.e. whether to load or not.
other than that we have our private custom method getRoutePermissions
which will give us the route data from ActivatedRouteSnapshot
and the checkPermission
method is linked with another service that will validate both the token-based session and also it will get the user-role from the server.
授权服务 (可选的)
我们已经了解AuthGuard,但是您可能对授权服务中包含的内容有疑问?
我在AuthGuard中使用的三种授权服务方法如下。
public getSession() : Promise<boolean> {
const session = localStorage.getItem('token');
return new Promise((resolve, reject) => {
if (session) {
return resolve(true);
} else {
return reject(false);
}
});
}
我们检查了登录后是否设置了令牌。
您必须使用JWT令牌,否则有人会通过手动在本地存储中放置价值来入侵您的系统。
public getUserRoless(): Promise<string[]> {
return new Promise((resolve, reject) => {
this.http.get(`${this.baseUrl}getUserRoles`)
.pipe(catchError((error: any, caught: any) => {
reject(error);
return caught;
}),
map((res: any) => res.data))
.subscribe((role: string[]) => {
resolve(role);
});
});
}
在这种方法中,我们只是获取登录的用户角色列表。
与每次路由更改一样,这将调用一个可能在服务器上造成巨大负担的API,而不是我们可以通过维护响应对象来使用缓存数据,或者我们可以缓存API响应。
进一步了解 : 我们如何在Angular 10中缓存HTTP请求?
public areUserRolesAllowed(userRoles: string[], allowedUserRoles: Roles[]): boolean {
for (const role of userRoles) {
for (const allowedRole of allowedUserRoles) {
if (role.toLowerCase() === allowedRole.toLowerCase()) {
return true;
}
}
}
return false;
}
此方法将仅迭代来自两个数组的数据,并检查其包含与否。