简体   繁体   English

Angular JWT 有少量角色访问

[英]Angular JWT with few Roles acess

I want to use several roles for accessing views in the application, if I use one role everything works correctly, however when I use several roles, the views do not give access我想使用多个角色来访问应用程序中的视图,如果我使用一个角色一切正常,但是当我使用多个角色时,视图不授予访问权限

My model User have this:我的 model 用户有这个:

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'
}

my backend give my token with with roles:我的后端给我的令牌与角色:

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

If I use this in login metod如果我在登录方法中使用它

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

i have only 1 role, and code work fine, but I can have many users who belong to different roles, and I need the application to give them access if there are at least 1 role我只有 1 个角色,代码工作正常,但我可以有许多属于不同角色的用户,如果至少有 1 个角色,我需要应用程序授予他们访问权限

I handle token decoding and getting roles in authorization service我在授权服务中处理令牌解码和获取角色

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;
    }))
} 

sigin metod for example例如 sigin 方法

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;
            });
  }

Not sure if I correctly specify multiple access roles不确定我是否正确指定了多个访问角色

//......
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 { }
//......

and guard sevice和警卫服务

  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;

} }

token in localStorage: localStorage 中的令牌:

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

change app-routing.module.ts更改 app-routing.module.ts

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

Error错误

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

TypeError: Cannot read property 'includes' of undefined TypeError:无法读取未定义的属性“包含”

It could also be that a typescript enum is not a string.也可能是 typescript 枚举不是字符串。 so comparing a enum with a string will never be true.所以将enum与字符串进行比较永远不会是真的。

What you need to use is a const enum because that compiles down to a string.您需要使用的是const enum ,因为它编译为字符串。

try changing to尝试更改为

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

Though this does have other implications.尽管这确实有其他含义。 https://www.typescriptlang.org/docs/handbook/enums.html#const-enums https://www.typescriptlang.org/docs/handbook/enums.html#const-enums

and instead of doing a indexOf you can use .includes而不是做一个 indexOf 你可以使用.includes

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

Edit: It could also be that your data is not inherited down to where you are trying to get it.编辑:也可能是您的数据没有继承到您尝试获取它的位置。

You might need to add this to your Router config您可能需要将此添加到您的路由器配置中

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

This really depends on how you are handling your AuthGuard code.这实际上取决于您如何处理 AuthGuard 代码。 There is a comprehensive tutorial on how to set up your Authentication and Authorization in this guide: https://jasonwatmore.com/post/2018/11/22/angular-7-role-based-authorization-tutorial-with-example本指南中有一个关于如何设置身份验证和授权的综合教程: https://jasonwatmore.com/post/2018/11/22/angular-7-role-based-authorization-tutorial-with-example

Big area where you could be experiencing the issue is on your AuthGuard .您可能遇到问题的大区域在您的AuthGuard上。 You can have this example from the link I shared above:您可以从我上面分享的链接中获得此示例:

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;
    }
}

You also need to make sure you are passing the right roles into your AuthGuard .您还需要确保将正确的角色传递到您的AuthGuard中。

If you want deeper restrictions in the future, there's also this guide: How to prevent actions by user role in Angular如果您将来想要更深入的限制,还有本指南: 如何防止 Angular 中的用户角色操作

Hope this helps!希望这可以帮助!

In your route config, there are some routes which don't need to check roles property in data.在您的路由配置中,有一些路由不需要检查数据中的角色属性。 Assuming everybody should have access to them.假设每个人都应该可以访问它们。

Change your auth guard to:-将您的身份验证保护更改为:-

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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM