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!
