我在同一个问题上卡住了很长一段时间,但最终想出了如何为 MySQL 做这件事。它的要点是您需要创建自己的模型,然后覆盖 @loopback/authentication-jwt 扩展中内置的 User 和 UserCredentials 的服务和模型。这个其实在github上有说明here
在我的例子中,让这件事变得不平凡的是,我需要弄清楚如何将扩展中定义的模型转换为您可以实际存储在 MySQL 中的数据模型。这样做后,如果它们位于模型文件夹中,它们将正确迁移。
以下是我为使其正常工作所做的一些示例:
在application.ts的构造函数中
// Mount authentication system
this.component(AuthenticationComponent);
// Mount jwt component
this.component(JWTAuthenticationComponent);
// Bind datasource
this.dataSource(NihongoWordListDataSource, UserServiceBindings.DATASOURCE_NAME);
// Bind user service
this.bind(UserServiceBindings.USER_SERVICE).toClass(AppUserService),
// Bind user and credentials repository
this.bind(UserServiceBindings.USER_REPOSITORY).toClass(
AppUserRepository,
),
this.bind(UserServiceBindings.USER_CREDENTIALS_REPOSITORY).toClass(
AppUserCredentialsRepository,
),
自定义用户服务(基本上是直接从github页面复制过来的,只有我的叫AppUser):
import {UserService} from '@loopback/authentication';
import {repository} from '@loopback/repository';
import {HttpErrors} from '@loopback/rest';
import {securityId, UserProfile} from '@loopback/security';
import {compare} from 'bcryptjs';
// User --> MyUser
import {AppUser} from '../models';
// UserRepository --> MyUserRepository
import { AppUserRepository } from '../repositories';
export type Credentials = {
email: string;
password: string;
};
// User --> MyUser
export class AppUserService implements UserService<AppUser, Credentials> {
constructor(
// UserRepository --> MyUserRepository
@repository(AppUserRepository) public userRepository: AppUserRepository,
) {}
// User --> MyUser
async verifyCredentials(credentials: Credentials): Promise<AppUser> {
const invalidCredentialsError = 'Invalid email or password.';
const foundUser = await this.userRepository.findOne({
where: {email: credentials.email},
});
if (!foundUser) {
throw new HttpErrors.Unauthorized(invalidCredentialsError);
}
const credentialsFound = await this.userRepository.findCredentials(
foundUser.id,
);
if (!credentialsFound) {
throw new HttpErrors.Unauthorized(invalidCredentialsError);
}
const passwordMatched = await compare(
credentials.password,
credentialsFound.password,
);
if (!passwordMatched) {
throw new HttpErrors.Unauthorized(invalidCredentialsError);
}
return foundUser;
}
// User --> MyUser
convertToUserProfile(user: AppUser): UserProfile {
return {
[securityId]: user.id.toString(),
name: user.username,
id: user.id,
email: user.email,
};
}
}
AppUser 模型:
import {Entity, model, property, hasOne} from '@loopback/repository';
import {AppUserCredentials} from './app-user-credentials.model';
@model({
settings: {
idInjection: false,
mysql: {schema: 'nihongo_word_list', table: 'app_user'}
}
})
export class AppUser extends Entity {
@property({
type: 'string',
id: true,
generated: false,
defaultFn: 'uuidv4',
mysql: {columnName: 'id', dataType: 'varchar', dataLength: 36, nullable: 'N'},
})
id: string;
@property({
type: 'string',
mysql: {columnName: 'realm', dataType: 'varchar', dataLength: 36, nullable: 'Y'},
})
realm?: string;
@property({
type: 'string',
mysql: {columnName: 'username', dataType: 'varchar', dataLength: 256, nullable: 'Y'},
})
username?: string;
@property({
type: 'string',
required: true,
mysql: {columnName: 'email', dataType: 'varchar', dataLength: 256, nullable: 'N'},
})
email: string;
@property({
type: 'boolean',
required: true,
mysql: {columnName: 'emailVerified', dataType: 'tinyint', dataLength: 1, nullable: 'Y', default: 0},
})
emailVerified?: boolean;
@property({
type: 'string',
mysql: {columnName: 'verificationToken', dataType: 'varchar', dataLength: 256, nullable: 'N'},
})
verificationToken?: string;
@hasOne(() => AppUserCredentials, {keyTo: 'userId'})
userCredentials: AppUserCredentials;
// Define well-known properties here
// Indexer property to allow additional data
// eslint-disable-next-line @typescript-eslint/no-explicit-any
[prop: string]: any;
constructor(data?: Partial<AppUser>) {
super(data);
}
}
export interface AppUserRelations {
// describe navigational properties here
}
export type AppUserWithRelations = AppUser & AppUserRelations;
您需要对 AppUserCredentials 执行相同的操作。
您还需要为 AppUser 和 AppUserCredentials 模型设置存储库。
我希望这能让您走上正轨。请注意,在对迁移进行更改时,您可以并且应该使用以下 npm CLI 方法。我刚刚这样做了:
// clean your dist directory otherwise built stuff gets left behind and can prevent your app from starting
npm run clean
// rebuild your code into dist folder
npm run build
// migrate your db
npm run migrate
祝你好运!