javascript - Angular JWT med några roller åtkomst


Jag vill använda flera roller för att få åtkomst till vyer i applikationen, om jag använder en roll fungerar allt korrekt, men när jag använder flera roller får vyerna ingen åtkomst

Min modell Användare har detta:

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

min backend ger min token med roller:

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

Om jag använder detta i inloggningsmetoden

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

Jag har bara 1 roll, och koden fungerar bra, men jag kan ha många användare som tillhör olika roller, och jag behöver att applikationen ger dem åtkomst om det finns minst 1 roll

Jag hanterar tokenavkodning och att få roller i auktoriseringstjänsten

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

inloggningsmetod till exempel

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

Osäker på om jag korrekt specificerar flera åtkomstroller

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

och bevakningstjänst

  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 i localStorage:

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

ändra app-routing.module.ts

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

Fel

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

TypeError: Kan inte läsa egenskapen 'includes' av undefined

Svar #1

Det kan också vara så att en TypeScript-enum inte är en sträng, så att jämföra en enum med en sträng kommer aldrig att vara sant.

Det du behöver använda är en const enum eftersom det kompileras ner till en sträng.

försök att byta till

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

Även om detta har andra implikationer. https://www.typescriptlang.org/docs/handbook/enums.html#const-enums

och istället för att använda indexOf kan du använda .includes

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

Redigera: Det kan också vara så att dina data inte ärvs ner till den plats där du försöker hämta dem.

Du kan behöva lägga till detta i din routerkonfiguration

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

Svar #2

Detta beror verkligen på hur du hanterar din AuthGuard-kod. Det finns en omfattande handledning om hur du ställer in din autentisering och auktorisering i denna guide: https://jasonwatmore.com/post/2018/11/22/angular-7-role-based-authorization-tutorial-with-example

Stort område där du kan uppleva problemet är på din AuthGuard. Du kan ha detta exempel från länken jag delade ovan:

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

Du behöver också se till att du skickar in rätt roller till din AuthGuard.

Om du vill ha djupare begränsningar i framtiden, finns det också denna guide: Hur man förhindrar åtgärder baserat på användarroll i Angular

Hoppas det här hjälper!

Relaterade artiklar