【问题标题】:Angular JWT with few Roles acess具有很少角色访问权限的 Angular JWT
【发布时间】:2020-09-13 05:57:24
【问题描述】:

我想使用多个角色来访问应用程序中的视图,如果我使用一个角色一切正常,但是当我使用多个角色时,视图不会授予访问权限

我的模型用户有这个:

export class User {
    role: Role[];                // I change - role: Role[] for few roles
    expiresIn: string;
    aud: string;
    iss: string;
    token?: string;
}

export const enum Role {
    Admin = 'admin',
    User = 'user',   
    Engineer = 'engineer'
}

我的后端用角色给我的令牌:

//....
role: (2) ["admin", "engineer"]
token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ
//....

如果我在登录方法中使用它

tokenInfo['http://schemas.microsoft.com/ws/2008/06/identity/claims/role'][0]   - first element in array

我只有 1 个角色,代码可以正常工作,但我可以拥有多个属于不同角色的用户,如果至少有 1 个角色,我需要应用程序授予他们访问权限

我在授权服务中处理令牌解码和获取角色

signin(username:string, password:string ) {
    return this.http.post<User>(`${environment.apiUrl}${environment.apiVersion}Profile/Login`, {username, password})    
    .pipe(map(user => {
    if (user && user.token) {
      let tokenInfo = this.getDecodedAccessToken(user.token); // decode token
      this.session = {
        token: user.token,
        role: tokenInfo['http://schemas.microsoft.com/ws/2008/06/identity/claims/role'],     - add this [0]
        expiresIn: tokenInfo.exp,
        aud: tokenInfo.aud,
        iss: tokenInfo.iss,            
      }
      localStorage.setItem('currentUser', JSON.stringify(this.session));
      this.currentUserSubject.next(this.session);         
    }
    return this.session;
    }))
} 

例如登录方法

Login() {
    this.auth.signin(this.signinForm.value.email, this.signinForm.value.password)
        .pipe(first())
        .subscribe(
            data => {
                console.log("User is logged in");
                this.router.navigate(['/dashboard']);
                this.loading = false;
            });
  }

不确定我是否正确指定了多个访问角色

//......
const adminRoutes: Routes = [
{
    path: 'dashboard',
    loadChildren: () => import('./views/dashboard/dashboard.module').then(m => m.DashboardModule),
    canActivate: [AuthGaurd],

},
{
    path: 'books',
    loadChildren: () => import('./views/books/books.module').then(m => m.BooksModule),
    canActivate: [AuthGaurd],
    data: { roles: [Role.Admin] }  <- work fine if 1 role
},
{
    path: 'person',
    loadChildren: () => import('./views/person/person.module').then(m => m.PersonModule),
    canActivate: [AuthGaurd],    
    data: { roles: [Role.Admin, Role.Engineer] }  <- if have 1 role - admin - open
 },
 {
    path: 'eqip',
    loadChildren: () => import('./views/eqip/eqip.module').then(m => m.PersonModule),
    canActivate: [AuthGaurd],    
    data: { roles: [Role.Engineer] }  <- not open becouse only admin role
 }];

const routes: Routes = [
{
    path: '',
    redirectTo: 'applayout-sidebar-compact/dashboard/v1',
    pathMatch: 'full',
},
...
{
    path: '**',
    redirectTo: 'others/404'
}];

@NgModule({
imports: [RouterModule.forRoot(routes, { useHash: true })],
exports: [RouterModule]
})
export class AppRoutingModule { }
//......

和守卫服务

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
const url: string = state.url;
const currentUser = this.auth.currentUserValue;    
// in auth.service.ts
// constructor(private http: HttpClient) {
//   this.currentUserSubject = new BehaviorSubject<User>(JSON.parse(localStorage.getItem('currentUser')));
//   this.currentUser = this.currentUserSubject.asObservable();

// }
// public get currentUserValue(): User {
//   return this.currentUserSubject.value;
// }
if (this.auth.isUserLoggedIn()) {


  // test code
  const ter = route.data.roles.includes(currentUser.role) <- Error now here
  console.log(ter)  



  // main check role code
  // if (route.data.roles && route.data.roles.indexOf(currentUser.role) === -1) {
  //   this.router.navigate(["/"]);
  //   return false;
  // }

  return true;

}
this.auth.setRedirectUrl(url);
this.router.navigate([this.auth.getLoginUrl()]);
return false;

}

本地存储中的令牌:

aud: "Service"
expiresIn: 1591967261
iss: "USs"
role: ["admin", "engineer"]
token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHR....

更改 app-routing.module.ts

@NgModule({
imports: [RouterModule.forRoot(routes, { 
    useHash: true,
    initialNavigation: 'enabled',
    paramsInheritanceStrategy: 'always',
    relativeLinkResolution: 'corrected',
    scrollPositionRestoration: 'enabled',
})],
exports: [RouterModule]

错误

Uncaught (in promise): TypeError: Cannot read property 'includes' of undefined

TypeError: 无法读取未定义的属性“包含”

【问题讨论】:

  • 能否提供您的 AuthGuard 源代码?
  • 请添加您的AuthGuard 代码。另外,tokenInfo 是什么样的?
  • 添加guard.service进行检查
  • 您在专门导航到仪表板时是否出现错误?
  • 仪表板路由没有 data.roles。

标签: javascript angular typescript angular-ui-router jwt


【解决方案1】:

也可能是打字稿枚举不是字符串。 所以将enum 与字符串进行比较永远不会是真的。

您需要使用const enum,因为它会编译为字符串。

试着改成

export const enum Role {
    Admin = 'admin',
    User = 'user',   
    Engineer = 'engineer'
}

虽然这确实有其他含义。 https://www.typescriptlang.org/docs/handbook/enums.html#const-enums

你可以使用.includes代替indexOf

route.data.roles.includes(currentUser.role)

编辑: 也可能是您的数据没有被继承到您尝试获取它的位置。

您可能需要将此添加到您的路由器配置中

RouterModule.forRoot([], {
      initialNavigation: 'enabled',
      paramsInheritanceStrategy: 'always', <-- this makes params and data accessible lower down into the tree
      relativeLinkResolution: 'corrected',
      scrollPositionRestoration: 'enabled',
    }),

【讨论】:

  • 添加 'const' 并更新导入,但如果尝试检查警卫中的角色 if (route.data.roles.indexOf(currentUser.role) === -1) { console.log('没有角色) }
  • 我添加了一个使用 .includes 的示例,它比 indexOf 更容易理解。但是您未定义 data.roles 的问题是因为您没有在路由配置中传递任何角色?
  • 现在无法读取未定义的属性“包含”,在路由中我有这个 - 数据:{角色:[Role.Engineer]}
  • 在尝试使用它之前确保 route.data 包含您的信息。但也可能是您的数据没有被继承下来。我已经用更多信息更新了我的答案
【解决方案2】:

这实际上取决于您如何处理 AuthGuard 代码。本指南中有一个关于如何设置身份验证和授权的综合教程:https://jasonwatmore.com/post/2018/11/22/angular-7-role-based-authorization-tutorial-with-example

您可能会遇到问题的主要区域是您的AuthGuard。您可以从我上面分享的链接中获得此示例:

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

import { AuthenticationService } from '@/_services';

@Injectable({ providedIn: 'root' })
export class AuthGuard implements CanActivate {
    constructor(
        private router: Router,
        private authenticationService: AuthenticationService
    ) {}

    canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
        const currentUser = this.authenticationService.currentUserValue;
        if (currentUser) {
            // check if route is restricted by role
            if (route.data.roles && route.data.roles.indexOf(currentUser.role) === -1) {
                // role not authorised so redirect to home page
                this.router.navigate(['/']);
                return false;
            }

            // authorised so return true
            return true;
        }

        // not logged in so redirect to login page with the return url
        this.router.navigate(['/login'], { queryParams: { returnUrl: state.url }});
        return false;
    }
}

您还需要确保将正确的角色传递给您的AuthGuard

如果您希望将来有更深的限制,还有本指南: How to prevent actions by user role in Angular

希望这会有所帮助!

【讨论】:

  • 我用这个例子来创建这个守卫
【解决方案3】:

在您的路由配置中,有一些路由不需要检查数据中的角色属性。假设每个人都应该可以访问它们。

将您的身份验证保护更改为:-

canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
    const url: string = state.url;
    const currentUser = this.auth.currentUserValue;
    console.log(currentUser);
    if (this.auth.isUserLoggedIn()) {
      if (!route.data.roles || route.data.roles.length === 0) {
        return true;
      }
      if (typeof currentUser.role === 'string' && route.data.roles.includes(currentUser.role)) {
        return true;
      }
      if (Array.isArray(currentUser.role)) {
        for (let i = 0; i < currentUser.role.length; i++) {
          if (route.data.roles.includes(currentUser.role[i])) {
            return true;
          }
        }
      }
      this.router.navigate([this.auth.getLoginUrl()]); //TODO: Change to 403 PAGE (403 forbidden)
      return false;
    }
    this.auth.setRedirectUrl(url);
    this.router.navigate([this.auth.getLoginUrl()]);
    return false;
}

【讨论】:

  • 在这种情况下应该返回什么,你能告诉我。请我实现的是,如果角色不匹配重定向到登录。
  • @ЯрославПрохоров 请告诉我如果角色不匹配该怎么办,我将更改我的代码。
  • 更新了我的代码,现在检查。如果不匹配,请告诉我预期的行为。
  • 它总是返回false,在最后一个例子中,应用程序进入了一个循环,根本不加载
  • @ЯрославПрохоров 我已经更新了答案。检查更新一个。还请告诉当前用户在控制台中打印的内容。我为此插入了一个 console.log 语句。
猜你喜欢
  • 2019-06-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-06
  • 2015-11-20
  • 1970-01-01
  • 2021-11-12
  • 2011-09-15
相关资源
最近更新 更多