Фактическая проблема заключалась в том, что при генерации токена роль свойства не была включена в TokenService
из @loopback/authentication
.
Итак, я создал службу пользовательских токенов, реализующую этот TokenService
, и добавил роль свойства в то время как генерация токена.
Таким образом, более поздняя проверка подлинности с обратной связью отправляет эту роль на авторизацию с обратной связью. Вы можете получить к нему доступ в AuthorizationContext.principals[0]
Вот код
custom-toekn.service.ts
import {TokenService} from '@loopback/authentication';
import {inject} from '@loopback/context';
import {HttpErrors} from '@loopback/rest';
import {securityId, UserProfile} from '@loopback/security';
import {promisify} from 'util';
import {TokenServiceBindings} from '../keys';
const jwt = require('jsonwebtoken');
const signAsync = promisify(jwt.sign);
const verifyAsync = promisify(jwt.verify);
export class JWTService implements TokenService {
constructor(
@inject(TokenServiceBindings.TOKEN_SECRET)
private jwtSecret: string,
@inject(TokenServiceBindings.TOKEN_EXPIRES_IN)
private jwtExpiresIn: string,
) {}
async verifyToken(token: string): Promise<UserProfile> {
if (!token) {
throw new HttpErrors.Unauthorized(
`Error verifying token : 'token' is null`,
);
}
let userProfile: UserProfile;
try {
// decode user profile from token
const decodedToken = await verifyAsync(token, this.jwtSecret);
// don't copy over token field 'iat' and 'exp', nor 'email' to user profile
userProfile = Object.assign(
{[securityId]: '', name: ''},
{
[securityId]: decodedToken.id,
name: decodedToken.name,
id: decodedToken.id,
role: decodedToken.role,
},
);
} catch (error) {
throw new HttpErrors.Unauthorized(
`Error verifying token : ${error.message}`,
);
}
return userProfile;
}
async generateToken(userProfile: UserProfile): Promise<string> {
if (!userProfile) {
throw new HttpErrors.Unauthorized(
'Error generating token : userProfile is null',
);
}
const userInfoForToken = {
id: userProfile[securityId],
name: userProfile.name,
role: userProfile.role,
};
// Generate a JSON Web Token
let token: string;
try {
token = await signAsync(userInfoForToken, this.jwtSecret, {
expiresIn: Number(this.jwtExpiresIn),
});
} catch (error) {
throw new HttpErrors.Unauthorized(`Error encoding token : ${error}`);
}
return token;
}
}
keys.ts
import {TokenService} from '@loopback/authentication';
export namespace TokenServiceConstants {
export const TOKEN_SECRET_VALUE = 'myjwts3cr3t';
export const TOKEN_EXPIRES_IN_VALUE = '600';
}
export namespace TokenServiceBindings {
export const TOKEN_SECRET = BindingKey.create<string>(
'authentication.jwt.secret',
);
export const TOKEN_EXPIRES_IN = BindingKey.create<string>(
'authentication.jwt.expires.in.seconds',
);
export const TOKEN_SERVICE = BindingKey.create<TokenService>(
'services.authentication.jwt.tokenservice',
);
}
Затем вам нужно привязать этот токен-сервис в application.ts
application.ts
import {JWTService} from './services/token-service';
import {TokenServiceBindings, TokenServiceConstants} from './keys';
this.bind(TokenServiceBindings.TOKEN_SECRET).to(
TokenServiceConstants.TOKEN_SECRET_VALUE,
);
this.bind(TokenServiceBindings.TOKEN_EXPIRES_IN).to(
TokenServiceConstants.TOKEN_EXPIRES_IN_VALUE,
);
this.bind(TokenServiceBindings.TOKEN_SERVICE).toClass(JWTService);
controller.ts
import {authenticate, TokenService, UserService} from '@loopback/authentication';
import {Credentials, OPERATION_SECURITY_SPEC, TokenServiceBindings, UserServiceBindings} from '@loopback/authentication-jwt';
import {authorize} from '@loopback/authorization';
export class UserController {
constructor(
@repository(UserRepository)
public userRepository: UserRepository,
@inject(TokenServiceBindings.TOKEN_SERVICE)
public jwtService: TokenService,
@inject(UserServiceBindings.USER_SERVICE)
public userService: UserService<User, Credentials>,
@inject(SecurityBindings.USER, {optional: true})
public users: UserProfile,
) {}
@authenticate('jwt')
@authorize({allowedRoles: ['admin'], voters: [basicAuthorization]})
aasync fund(){}
}