如何在 Angular 角色守卫中等待服务器授权?
How to wait for server authorization in an Angular role guard?
在我的应用程序中,用户登录并收到一个 JWT,该 JWT 存储在本地存储中。用户通过身份验证后,将调用服务器以确定用户的角色和功能(他们可以访问哪些页面)。
我的问题是,当用户想要打开一个页面(恢复旧会话、复制标签、将 URL 传递给其他人等)时,应用程序没有授权详细信息,必须先请求他们,角色守卫开始。这导致用户被重定向到登录页面。
@Injectable({
providedIn: 'root'
})
export class RoleGuardService implements CanActivate {
constructor(public auth: AuthService, public router: Router, public globalConfig: GlobalConfigService) { }
canActivate(route: ActivatedRouteSnapshot): boolean {
if (!this.auth.isAuthenticated()) {
this.router.navigate(['login']);
return false;
}
const expectedFunction = route.data.expectedFunction;
if (!this.globalConfig.hasFunction(expectedFunction)) {
this.router.navigate(['login']);
return false;
}
return true;
}
}
路由中定义了预期的功能,例如:
{
path: 'system-admin', loadChildren: () => SystemAdminModule,
data: { breadcrumb: 'System Admin', expectedFunction: FunctionType.SystemAdministration }, canActivate: [RoleGuard]
},
GlobalConfigService
中的 hasFunction
正文如下所示:
private authorizedUser: AuthorizedUser = new AuthorizedUser();
public hasFunction(expectedFunction: FunctionType): boolean {
return !!this.authorizedUser.functions
&& this.authorizedUser.functions.find(f => f === expectedFunction) !== undefined;
}
在AuthService
中完成的授权如下:
public onAuthorized = new Subject<AuthorizedUser>();
authorize() {
const url = environment.APIURL + 'auth/currentuser';
return this.http.get(url).subscribe(
resp => {
this.globalConfig.AuthorizedUser = resp;
this.onAuthorized.next(resp as AuthorizedUser);
}
);
}
而 authorize()
是从 ngOnInit()
在 AppComponent
中调用的
ngOnInit(): void {
if (this.auth.isAuthenticated()) {
this.auth.authorize();
} else {
this.router.navigate(['login']);
}
}
我认为解决方案是在用户通过身份验证后进入某种等待状态,然后在评估任何其他内容之前应允许完成授权。这需要仅在 RoleGuard
中发生,还是会跨越整个 authentication/authorization 过程?
是的,你可以在你的守卫里面等待用户授权。唯一需要记住的是不要对用户进行两次授权,这意味着您应该在页面导航之间缓存授权结果。
作用-guard.service.ts
canActivate(route: ActivatedRouteSnapshot): boolean | Promise<boolean> {
if (!this.auth.isAuthenticated()) {
this.router.navigate(['login']);
return false;
}
return this.auth.authorize().then(result => {
if (!result) {
return false;
}
const expectedFunction = route.data.expectedFunction;
if (!this.globalConfig.hasFunction(expectedFunction)) {
this.router.navigate(['login']);
return false;
}
return true;
});
}
auth.service.ts
@Injectable({
providedIn: 'root',
})
class AuthService {
...
private authorizePromise: Promise<boolean>;
constructor(private http: HttpClient, private globalConfig: GlobalConfigService) {}
authorize(): Promise<boolean> {
if (!this.authorizePromise) {
const url = environment.APIURL + 'auth/currentuser';
this.authorizePromise = this.http.get(url)
.toPromise()
.then(resp => {
this.globalConfig.AuthorizedUser = resp;
this.onAuthorized.next(resp as AuthorizedUser);
return true;
})
.catch(() => false);
}
return this.authorizePromise;
}
}
如您所见,我在 AuthService 中使用了缓存 authorizePromise
来缓存授权结果,这样授权只会发生一次。
这里还有一些片段 live example
在我的应用程序中,用户登录并收到一个 JWT,该 JWT 存储在本地存储中。用户通过身份验证后,将调用服务器以确定用户的角色和功能(他们可以访问哪些页面)。
我的问题是,当用户想要打开一个页面(恢复旧会话、复制标签、将 URL 传递给其他人等)时,应用程序没有授权详细信息,必须先请求他们,角色守卫开始。这导致用户被重定向到登录页面。
@Injectable({
providedIn: 'root'
})
export class RoleGuardService implements CanActivate {
constructor(public auth: AuthService, public router: Router, public globalConfig: GlobalConfigService) { }
canActivate(route: ActivatedRouteSnapshot): boolean {
if (!this.auth.isAuthenticated()) {
this.router.navigate(['login']);
return false;
}
const expectedFunction = route.data.expectedFunction;
if (!this.globalConfig.hasFunction(expectedFunction)) {
this.router.navigate(['login']);
return false;
}
return true;
}
}
路由中定义了预期的功能,例如:
{
path: 'system-admin', loadChildren: () => SystemAdminModule,
data: { breadcrumb: 'System Admin', expectedFunction: FunctionType.SystemAdministration }, canActivate: [RoleGuard]
},
GlobalConfigService
中的 hasFunction
正文如下所示:
private authorizedUser: AuthorizedUser = new AuthorizedUser();
public hasFunction(expectedFunction: FunctionType): boolean {
return !!this.authorizedUser.functions
&& this.authorizedUser.functions.find(f => f === expectedFunction) !== undefined;
}
在AuthService
中完成的授权如下:
public onAuthorized = new Subject<AuthorizedUser>();
authorize() {
const url = environment.APIURL + 'auth/currentuser';
return this.http.get(url).subscribe(
resp => {
this.globalConfig.AuthorizedUser = resp;
this.onAuthorized.next(resp as AuthorizedUser);
}
);
}
而 authorize()
是从 ngOnInit()
在 AppComponent
ngOnInit(): void {
if (this.auth.isAuthenticated()) {
this.auth.authorize();
} else {
this.router.navigate(['login']);
}
}
我认为解决方案是在用户通过身份验证后进入某种等待状态,然后在评估任何其他内容之前应允许完成授权。这需要仅在 RoleGuard
中发生,还是会跨越整个 authentication/authorization 过程?
是的,你可以在你的守卫里面等待用户授权。唯一需要记住的是不要对用户进行两次授权,这意味着您应该在页面导航之间缓存授权结果。
作用-guard.service.ts
canActivate(route: ActivatedRouteSnapshot): boolean | Promise<boolean> {
if (!this.auth.isAuthenticated()) {
this.router.navigate(['login']);
return false;
}
return this.auth.authorize().then(result => {
if (!result) {
return false;
}
const expectedFunction = route.data.expectedFunction;
if (!this.globalConfig.hasFunction(expectedFunction)) {
this.router.navigate(['login']);
return false;
}
return true;
});
}
auth.service.ts
@Injectable({
providedIn: 'root',
})
class AuthService {
...
private authorizePromise: Promise<boolean>;
constructor(private http: HttpClient, private globalConfig: GlobalConfigService) {}
authorize(): Promise<boolean> {
if (!this.authorizePromise) {
const url = environment.APIURL + 'auth/currentuser';
this.authorizePromise = this.http.get(url)
.toPromise()
.then(resp => {
this.globalConfig.AuthorizedUser = resp;
this.onAuthorized.next(resp as AuthorizedUser);
return true;
})
.catch(() => false);
}
return this.authorizePromise;
}
}
如您所见,我在 AuthService 中使用了缓存 authorizePromise
来缓存授权结果,这样授权只会发生一次。
这里还有一些片段 live example