1. TypeORM 连接 MySQL
安装依赖:
npm install --save typeorm mysql2 @nestjs/typeorm
1.1 配置环境变量
npm i @nestjs/config --save
创建.env文件
MYSQL_HOST='localhost'
MYSQL_PORT=3306
MYSQL_USERNAME='root'
MYSQL_PASSWORD='xxxx'
# 要连接的数据库名称
MYSQL_DATABASE='test'
全局加载环境变量
import { Module } from '@nestjs/common'
import { TypeOrmModule } from '@nestjs/typeorm'
import { ConfigModule } from '@nestjs/config'
import { AppController } from './app.controller'
import { AppService } from './app.service'
import config from './common/config'
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
load: [config],
}),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
1.2 TypeORM注册
app.module.ts
import { Module } from '@nestjs/common'
import { TypeOrmModule } from '@nestjs/typeorm'
import { ConfigModule } from '@nestjs/config'
import { AppController } from './app.controller'
import { AppService } from './app.service'
import config from './common/config'
@Module({
imports: [
// 配置全局的环境变量
ConfigModule.forRoot({
isGlobal: true,
load: [config],
}),
// 连接MySQL
TypeOrmModule.forRoot({
type: 'mysql',
host: process.env.MYSQL_HOST,
port: process.env.MYSQL_PORT as unknown as number,
username: process.env.MYSQL_USERNAME,
password: process.env.MYSQL_PASSWORD,
database: process.env.MYSQL_DATABASE,
synchronize: true,
// 自动加载实体
autoLoadEntities: true,
}),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
1.3 创建实体
关联关系:
- 用户 <=> 角色 (1对多)
- 角色 <=> 权限 (多对多)
User实体:
import { Role } from 'src/role/entities/role.entity'
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number
@Column()
username: string
@Column()
password: string
// 一个用户对应一个角色,一个角色对应多个用户
@ManyToOne(() => Role, (role) => role.user)
@JoinColumn({ name: 'roleId' })
role: Role
}
Role(角色)实体:
import { Permission } from 'src/permission/entities/permission.entity'
import { User } from 'src/user/entities/user.entity'
@Entity()
export class Role {
@PrimaryGeneratedColumn()
id: number
@Column()
name: string
@OneToMany(() => User, (user) => user.role)
@JoinColumn({ name: 'userId' })
user: User
@ManyToMany(() => Permission)
@JoinTable()
permissions: Permission[]
}
Permission实体:
@Entity()
export class Permission {
@PrimaryGeneratedColumn()
id: number
@Column()
name: string
}
在role.module.ts中进行关联:
@Module({
imports: [TypeOrmModule.forFeature([User, Role, Permission])],
controllers: [RoleController],
providers: [RoleService],
})
export class RoleModule {}
在user.module.ts中进行关联:
@Module({
imports: [TypeOrmModule.forFeature([Role, User])],
controllers: [UserController],
providers: [UserService],
})
export class UserModule {}
1.4 Database Client
查看TypeORM生成的表,这里通过VsCode的插件Database Client
2. 角色和权限设计
角色:
- 用户
- 管理员
权限(模块:操作):
- 用户:Query
- 管理员:CRUD
数据表示例:
3. 具体实现
创建auth模块用于权限验证
// auth.controller.ts
import { AuthService } from './auth.service'
@Controller('auth')
export class AuthController {
constructor(private readonly authService: AuthService) {}
@Post('permission')
async hasPermission(@Body() body) {
const { userId, permissionName } = body
return this.authService.hasPermission(userId, permissionName)
}
}
具体验证逻辑:
import { RoleService } from 'src/role/role.service'
import { UserService } from 'src/user/user.service'
@Injectable()
export class AuthService {
// 注意:这里用到的service需要在原来的module.ts的exports进行导出,并在auth.module.ts中进行引入
constructor(
private readonly userService: UserService,
private readonly roleService: RoleService,
) {}
async hasPermission(userId: number, permissionName: string) {
// 根据用户id查询用户信息
const user = await this.userService.findOne(userId)
const { password, ...result } = user
// 根据用户的角色id查询该角色对应的权限列表
const { permissions } = await this.roleService.findOne(result.role.id)
// 权限校验
if (permissions && permissions.find((p) => p.name === permissionName)) {
return true
}
return false
}
}
一对多关联查询:
// user.service.ts
import { Injectable } from '@nestjs/common'
import { InjectRepository } from '@nestjs/typeorm'
import { Repository } from 'typeorm'
import { User } from './entities/user.entity'
@Injectable()
export class UserService {
constructor(
@InjectRepository(User) private readonly userRepo: Repository<User>,
) {}
async findOne(id: number) {
return this.userRepo.findOneOrFail({
where: {
id,
},
relations: ['role'],
})
}
}
import { Injectable } from '@nestjs/common'
import { InjectRepository } from '@nestjs/typeorm'
import { Repository } from 'typeorm'
import { Role } from './entities/role.entity'
@Injectable()
export class RoleService {
constructor(
@InjectRepository(Role) private readonly roleRepo: Repository<Role>,
) {}
async findOne(id: number) {
return this.roleRepo.findOneOrFail({
where: {
id,
},
relations: ['permissions'],
})
}
}
4. ApiFox 测试
为了测试,直接在user表添加两条数据,并分别给定角色为管理员(1)和用户(2); 并且,前面提到过用户只允许查询、管理员拥有所有权限,因此还需要在连接表中分配对应的权限;
用户表:
连接表:分配权限
管理员测试:
用户测试: