【问题标题】:Loopback4 authorization user property role is always undefinedLoopback4 授权用户属性角色始终未定义
【发布时间】:2020-05-27 17:02:45
【问题描述】:

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

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

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

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

提前谢谢你

定制服务

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

}

authorizer.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
  }
}

【问题讨论】:

    标签: loopback4


    【解决方案1】:

    实际问题是在生成令牌时,TokenService 中没有包含来自@loopback/authentication 的属性角色。

    所以我创建了自定义令牌服务来实现这个TokenService,并在生成令牌时添加了一个属性角色。

    所以后面的 loopback-authentication 会将这个角色发送给 loopback-authorization。您可以在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中绑定这个token-service

    应用程序.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(){}
    
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-05-18
      • 2012-03-19
      • 1970-01-01
      • 1970-01-01
      • 2012-07-14
      • 2014-01-23
      • 1970-01-01
      相关资源
      最近更新 更多