环回4 授权用户属性角色始终未定义



我已经在我的 lb4 应用程序中成功实现了 jwt auth,使用自定义服务实现了来自@loopback/authentication-jwt的用户服务。身份验证一切正常。

但是当我进行授权时AuthorizationMetadata只包含两个字段 - id 和名称。在发行令牌时,我使用了许多字段,其中一个是role

但是现在,所有其他字段(如角色或电子邮件(都未定义。因此,每当我尝试访问受授权装饰器保护的控制器时,我都会被拒绝 501 访问。

我不明白为什么其他属性未定义。

提前谢谢你

定制服务

export class CustomUserService implements UserService<User, Credentials> {
// other code
convertToUserProfile(user: User): UserProfile {
let address = ''
if (user.address) {
address = user.address
}
const profile = {
[securityId]: user.id!.toString(),
name: user.name,
id: user.id,
email: user.email,
role: user.role,
address: user.address
}
console.log(profile)
return profile
}
}

登录控制器

import {authenticate, TokenService, UserService} from '@loopback/authentication';
export class UserController {
constructor(@inject(SecurityBindings.USER, {optional: true})
public users: UserProfile,){}
//@get login
async login(
@requestBody(CredentialsRequestBody) credentials: Credentials,
): Promise<{token: string}> {
// ensure the user exists, and the password is correct
const user = await this.userService.verifyCredentials(credentials);
// convert a User object into a UserProfile object (reduced set of properties)
const userProfile = this.userService.convertToUserProfile(user);
// create a JSON Web Token based on the user profile
const token = await this.jwtService.generateToken(userProfile);
return {token};
}

授权方.ts

import {AuthorizationContext, AuthorizationDecision, AuthorizationMetadata} from '@loopback/authorization';
export async function basicAuthorization(
authorizationCtx: AuthorizationContext,
metadata: AuthorizationMetadata,
): Promise<AuthorizationDecision> {
// No access if authorization details are missing
let currentUser: UserProfile;
if (authorizationCtx.principals.length > 0) {
const user = _.pick(authorizationCtx.principals[0]
, [
'id',
'name',
'role',
'email',
'address'
]);
console.log(user) // contains only id and name
// other code
}
}

实际问题是在生成令牌时,属性角色不包含在@loopback/authenticationTokenService中。

因此,我创建了实现此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;
}
}

键.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中绑定此令牌服务

应用程序.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);

控制器.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(){}
}

最新更新