Angular 角色访问权限很少的 JWT
Angular JWT with few Roles acess
我想使用多个角色来访问应用程序中的视图,如果我使用一个角色一切正常,但是当我使用多个角色时,视图不提供访问权限
我的模型用户有这个:
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....
更改应用-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: 无法读取未定义的 属性 'includes'
这实际上取决于您处理 AuthGuard 代码的方式。本指南中有一个关于如何设置身份验证和授权的综合教程:https://jasonwatmore.com/post/2018/11/22/angular-7-role-based-authorization-tutorial-with-example
您可能遇到问题的主要区域是 AuthGuard
。你可以从我上面分享的link中得到这个例子:
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
。
如果您以后想要更深入的限制,还有这个指南:
希望对您有所帮助!
也可能是打字稿枚举不是字符串。
所以将 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',
}),
在您的路由配置中,有些路由不需要检查数据中的角色属性。假设每个人都应该可以访问它们。
将您的身份验证更改为:-
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;
}
我想使用多个角色来访问应用程序中的视图,如果我使用一个角色一切正常,但是当我使用多个角色时,视图不提供访问权限
我的模型用户有这个:
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....
更改应用-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: 无法读取未定义的 属性 'includes'
这实际上取决于您处理 AuthGuard 代码的方式。本指南中有一个关于如何设置身份验证和授权的综合教程:https://jasonwatmore.com/post/2018/11/22/angular-7-role-based-authorization-tutorial-with-example
您可能遇到问题的主要区域是 AuthGuard
。你可以从我上面分享的link中得到这个例子:
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
。
如果您以后想要更深入的限制,还有这个指南:
希望对您有所帮助!
也可能是打字稿枚举不是字符串。
所以将 enum
与字符串进行比较永远不会是真的。
您需要使用的是 const enum
,因为它会编译成字符串。
尝试更改为
export const enum Role {
Admin = 'admin',
User = 'user',
Engineer = 'engineer'
}
尽管这确实有其他含义。 https://www.typescriptlang.org/docs/handbook/enums.html#const-enums
您可以使用 .includes
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',
}),
在您的路由配置中,有些路由不需要检查数据中的角色属性。假设每个人都应该可以访问它们。
将您的身份验证更改为:-
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;
}