【问题标题】:Angular authentication wait to set permissions in local storage before guardAngular 身份验证在守卫之前等待在本地存储中设置权限
【发布时间】:2019-11-30 04:42:50
【问题描述】:

我尝试在我的项目中实现身份验证服务。 后端为我提供 sso 令牌和当前用户权限。

{
    "success":true,
    "data: [
            {
             "first_name":"Michel",
             "last_name":"Drucker",
             "email":"mdrucker@fr3.com",
             "company":"fr3",
             "image":"http://micheldrucker.com/moi.jpg",
             "permissions":["ADMIN"]
            }
           ]
}

我想在本地存储中设置这些数据,但我的守卫不会等待这些数据被设置。 所以我在警卫中的验证不起作用,我有一个错误 Cannot read property 'data' of null.

为了防止这个错误,我设置了一个item的权限,之前处理像localStorage.setItem('permissions', 'xxx');

app.component.ts

使用事件快照保存来自 url 的令牌

this.router.events.subscribe((event: Event) => {
            // put sso and permissions in local storage
            if (event instanceof ChildActivationStart) {
                if (event.snapshot.queryParams.sso !== undefined) {
                    localStorage.setItem('sso', event.snapshot.queryParams.sso);
                    this.apiService.permissions().subscribe((data) => {
                        localStorage.setItem('permissions', data.data[0].permissions);
                    });
                }
            }
        });

authorization.guard.ts

检查路由权限规则,如果未授权则阻止用户

canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
        // declaration of route roles path
        const allowedRoles = next.data.permissions.only;
        // check if user is authorized
        const isAuthorized = this.authorizationService.isAuthorized(allowedRoles);

        if (!isAuthorized) {
            this.snackbarMessage.openErrorSnackBar(this.translate.instant('GUARD.UNAUTHORIZED'));
            this.router.navigate(['/']);
        }

        return isAuthorized;
    }

authorization.service.ts

控制用户

isAuthorized(allowedRoles: string[]): boolean {
        // check if the list of allowed roles is empty, if empty, user can access
        if (allowedRoles == null || allowedRoles.length === 0) {
            return true;
        }
        // get permissions stored at app init after login
        const permissions = localStorage.getItem('permissions');

        // if it's not exists, delete sso token and go back to login
        if (!permissions) {
           localStorage.removeItem('sso');
           this.router.navigate(['/login']);
        }

        // check if user has route allowed roles
        const rolesIsFound = allowedRoles.filter(element => permissions.includes(element));

        let userAuthorized = false;

        if (rolesIsFound.length > 0) {
            userAuthorized = true;
        }

        return userAuthorized;
    }

route.ts

{
        path: '',
        component: FuseHomeComponent,
        canActivate: [AuthorizationGuard],
        data: {
            permissions: {
                only: ['ADMIN'],
            }
        }
    },

auth-interceptor.ts

    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        const sso = localStorage.getItem('sso');
        if (sso) {
            request = request.clone({
                setHeaders: {
                    'Content-Type': 'application/json',
                    'X-API-KEY': sso,
                }
            });
        }

        return next.handle(request).pipe( tap(() => {},
            (err: any) => {
                if (err instanceof HttpErrorResponse) {
                    if (err.status !== 401) {
                        return;
                    }
                    localStorage.removeItem('sso');
                    localStorage.removeItem('permissions');
                    this.snackbarMessage.openErrorSnackBar(this.translate.instant('GUARD.UNAUTHORIZED'));
                    this.router.navigate(['/login']);
                }
            }));
    }

我不认为我的代码很好,将数据存储在应用程序组件中似乎不太好。通过设置权限“xxx”,用户可以在几秒钟内访问应用程序,直到 http 响应禁止用户访问。我认为这只是一个“等待”的问题。

【问题讨论】:

    标签: angular authentication wait guard


    【解决方案1】:

    您可以更改您的 authorization.service.ts 以返回 boolean observable 而不是 boolean 然后您的 authorization.guard.ts 将等到 observable 解决。 您还可以在获得 401 HTTP 状态时将代码从本地存储中清除 SSO 令牌到拦截器。

    authorization.service.ts

    isAuthorized(allowedRoles: string[]): Observable<boolean> { 
    
     return new Observable(observer => {
        const permissions = localStorage.getItem('permissions');
        if (!permissions) {
            this.apiService.permissions().subscribe((data) => {
                const permissions = data.data[0].permissions;
                localStorage.setItem('permissions', permissions);
                observer.next(this.hasValidRole(permissions));
                observer.complete();
            });
        } else {
            observer.next(this.hasValidRole(permissions));
            observer.complete();
        }
      });
    }
    
    
    hasValidRole(permissions) {
        // check if user has route allowed roles
       const rolesIsFound = allowedRoles.filter(element => permissions.includes(element));
    
       let userAuthorized = false;
    
       if (rolesIsFound.length > 0) {
         userAuthorized = true;
       }
    
       return userAuthorized;
    }
    

    【讨论】:

    • 感谢您的回复。我还有一个拦截器,它从 API 捕获 401 响应。我想知道在拦截器内的 url 中而不是在我的应用程序组件中获取 sso 是否更好?
    • 如果您明确调用端点来获取 SSO 令牌,则无需在拦截器中获取它。只保留拦截器中任何请求可能发生的逻辑,例如,如果您的后端自动刷新任何 HTTP 请求上的令牌,那么您可以使用代码在拦截器中获取 SSO。
    • 我仍然无法在授权服务的第 19 行读取 null 的属性“数据”。看起来 api 调用 this.apiService.permissions() 不再起作用了
    • 我认为这 Cannot read property 'data' of null 是因为我的拦截器,我在其中放置了标题:const sso = localStorage.getItem('sso'); if (sso) { request = request.clone({ setHeaders: { 'Content-Type': 'application/json', 'X-API-KEY': sso, } }); } 我不知道..
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-11
    • 1970-01-01
    • 2018-06-30
    • 1970-01-01
    • 2020-07-23
    相关资源
    最近更新 更多