feat: 调整项目层级
This commit is contained in:
30
server/src/modules/system/auth/auth.controller.ts
Normal file
30
server/src/modules/system/auth/auth.controller.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { Controller, Post, Body, Param } from '@nestjs/common';
|
||||
import { AuthService } from './auth.service';
|
||||
import { AuthDto } from './dto/auth.dto';
|
||||
import { SkipAuth } from '@/common/decorators/skip-auth.decorator';
|
||||
import { UserRoleDto } from './dto/user-role.dto';
|
||||
import { RolePermissionDto } from './dto/role-permission.dto';
|
||||
|
||||
@Controller()
|
||||
export class AuthController {
|
||||
constructor(private readonly authService: AuthService) {}
|
||||
|
||||
// 用户登录
|
||||
@Post('login')
|
||||
@SkipAuth()
|
||||
signIn(@Body() authDto: AuthDto) {
|
||||
return this.authService.signIn(authDto);
|
||||
}
|
||||
|
||||
// 授权用户角色
|
||||
@Post('userRole/:id')
|
||||
grantUserRole(@Param('id') id: string, @Body() dto: UserRoleDto) {
|
||||
return this.authService.grantUserRole(+id, dto);
|
||||
}
|
||||
|
||||
// 授权角色权限
|
||||
@Post('rolePermission/:id')
|
||||
grantRolePermission(@Param('id') id: string, @Body() dto: RolePermissionDto) {
|
||||
return this.authService.grantRolePermission(+id, dto);
|
||||
}
|
||||
}
|
||||
26
server/src/modules/system/auth/auth.module.ts
Normal file
26
server/src/modules/system/auth/auth.module.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { AuthService } from './auth.service';
|
||||
import { AuthController } from './auth.controller';
|
||||
import { UserModule } from '../user/user.module';
|
||||
import { JwtModule } from '@nestjs/jwt';
|
||||
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
UserModule,
|
||||
JwtModule.registerAsync({
|
||||
imports: [ConfigModule],
|
||||
inject: [ConfigService],
|
||||
global: true,
|
||||
useFactory: async (config: ConfigService) => {
|
||||
return {
|
||||
secret: config.get('jwt.secret'),
|
||||
signOptions: { expiresIn: config.get('jwt.expiresIn') },
|
||||
};
|
||||
},
|
||||
}),
|
||||
],
|
||||
controllers: [AuthController],
|
||||
providers: [AuthService],
|
||||
})
|
||||
export class AuthModule {}
|
||||
136
server/src/modules/system/auth/auth.service.ts
Normal file
136
server/src/modules/system/auth/auth.service.ts
Normal file
@@ -0,0 +1,136 @@
|
||||
import {
|
||||
ForbiddenException,
|
||||
HttpException,
|
||||
HttpStatus,
|
||||
Inject,
|
||||
Injectable,
|
||||
UnauthorizedException,
|
||||
} from '@nestjs/common';
|
||||
import { JwtService } from '@nestjs/jwt';
|
||||
import { AuthDto } from './dto/auth.dto';
|
||||
import * as bcrypt from 'bcryptjs';
|
||||
import { PrismaService } from '@/prisma/prisma.service';
|
||||
import { UserRoleDto } from './dto/user-role.dto';
|
||||
import { RolePermissionDto } from './dto/role-permission.dto';
|
||||
import { RedisGroup } from '@/common/enum';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { Cache, CACHE_MANAGER } from '@nestjs/cache-manager';
|
||||
import { nanoid } from 'nanoid';
|
||||
|
||||
@Injectable()
|
||||
export class AuthService {
|
||||
constructor(
|
||||
private readonly jwtService: JwtService,
|
||||
private readonly prisma: PrismaService,
|
||||
private readonly configService: ConfigService,
|
||||
@Inject(CACHE_MANAGER) private cacheService: Cache,
|
||||
) {}
|
||||
|
||||
// 登录
|
||||
async signIn({ account, password }: AuthDto) {
|
||||
const user = await this.prisma.sysUser.findUnique({
|
||||
where: {
|
||||
account,
|
||||
},
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
throw new UnauthorizedException('账号或密码错误!');
|
||||
}
|
||||
|
||||
const pass = await bcrypt.hash(password, user.passwordSalt);
|
||||
if (user?.password !== pass) {
|
||||
throw new UnauthorizedException('账号或密码错误!');
|
||||
}
|
||||
|
||||
if (user.status === 0) {
|
||||
throw new ForbiddenException('此账户已被禁用!');
|
||||
}
|
||||
|
||||
const jti = nanoid();
|
||||
|
||||
const payload = { sub: user.id, account: user.account, jti };
|
||||
|
||||
const accessToken = await this.jwtService.signAsync(payload);
|
||||
|
||||
await this.cacheService.set(
|
||||
`${RedisGroup.JwtJti}:${user.id}:${jti}`,
|
||||
user.id,
|
||||
this.configService.get('jwt.redisExpiresIn'),
|
||||
);
|
||||
|
||||
return { accessToken };
|
||||
}
|
||||
|
||||
async grantUserRole(id: number, { roleIds }: UserRoleDto) {
|
||||
const user = await this.prisma.sysUser.findUnique({
|
||||
where: { id },
|
||||
});
|
||||
if (!user) throw new HttpException('用户不存在!', HttpStatus.NOT_FOUND);
|
||||
|
||||
// 查询所有角色id
|
||||
const allRoles = await this.prisma.sysRole.findMany({
|
||||
select: { id: true },
|
||||
});
|
||||
|
||||
const allRoleIds = allRoles.map((item) => item.id);
|
||||
|
||||
const isContain = roleIds.every((item) => allRoleIds.includes(item));
|
||||
|
||||
if (!isContain)
|
||||
throw new HttpException(
|
||||
'出现不存在的角色id,请检查传入的roleIds!',
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
|
||||
const data = roleIds.map((item) => ({
|
||||
userId: id,
|
||||
roleId: item,
|
||||
}));
|
||||
|
||||
await this.prisma.$transaction(async (prisma) => {
|
||||
await prisma.sysUserRole.deleteMany({
|
||||
where: { userId: id },
|
||||
});
|
||||
await prisma.sysUserRole.createMany({ data });
|
||||
});
|
||||
}
|
||||
|
||||
async grantRolePermission(id: number, { permissionIds }: RolePermissionDto) {
|
||||
const role = await this.prisma.sysRole.findUnique({
|
||||
where: { id },
|
||||
});
|
||||
if (!role)
|
||||
throw new HttpException('角色信息不存在!', HttpStatus.NOT_FOUND);
|
||||
|
||||
// 查询所有权限
|
||||
const allPermission = await this.prisma.sysPermission.findMany({
|
||||
select: { id: true },
|
||||
});
|
||||
|
||||
const allPermissionIds = allPermission.map((item) => item.id);
|
||||
|
||||
// 判断传入的id是否都存在于数据库中
|
||||
const isContain = permissionIds.every((item) =>
|
||||
allPermissionIds.includes(item),
|
||||
);
|
||||
|
||||
if (!isContain)
|
||||
throw new HttpException(
|
||||
'出现不存在的权限id,请检查传入的permissionIds!',
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
|
||||
const data = permissionIds.map((permissionId) => ({
|
||||
roleId: id,
|
||||
permissionId,
|
||||
}));
|
||||
|
||||
await this.prisma.$transaction([
|
||||
this.prisma.sysRolePermission.deleteMany({
|
||||
where: { permissionId: { in: permissionIds } },
|
||||
}),
|
||||
this.prisma.sysRolePermission.createMany({ data }),
|
||||
]);
|
||||
}
|
||||
}
|
||||
7
server/src/modules/system/auth/dto/auth.dto.ts
Normal file
7
server/src/modules/system/auth/dto/auth.dto.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { CreateUserDto } from '@/modules/system/user/dto/create-user.dto';
|
||||
import { PickType } from '@nestjs/mapped-types';
|
||||
|
||||
export class AuthDto extends PickType(CreateUserDto, [
|
||||
'account',
|
||||
'password',
|
||||
] as const) {}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { ArrayUnique, IsInt, IsNotEmpty } from 'class-validator';
|
||||
|
||||
export class RolePermissionDto {
|
||||
@IsNotEmpty()
|
||||
@IsInt({ each: true })
|
||||
@ArrayUnique()
|
||||
permissionIds: number[];
|
||||
}
|
||||
8
server/src/modules/system/auth/dto/user-role.dto.ts
Normal file
8
server/src/modules/system/auth/dto/user-role.dto.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { ArrayUnique, IsInt, IsNotEmpty } from 'class-validator';
|
||||
|
||||
export class UserRoleDto {
|
||||
@IsNotEmpty()
|
||||
@IsInt({ each: true })
|
||||
@ArrayUnique()
|
||||
roleIds: number[];
|
||||
}
|
||||
22
server/src/modules/system/auth/roles.guard.ts
Normal file
22
server/src/modules/system/auth/roles.guard.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
|
||||
import { Reflector } from '@nestjs/core';
|
||||
import { Role } from '@/common/enum';
|
||||
import { ROLES_KEY } from '@/common/decorators/roles.decorator';
|
||||
|
||||
@Injectable()
|
||||
export class RolesGuard implements CanActivate {
|
||||
constructor(private reflector: Reflector) {}
|
||||
|
||||
canActivate(context: ExecutionContext): boolean {
|
||||
const requiredRoles = this.reflector.getAllAndOverride<Role[]>(ROLES_KEY, [
|
||||
context.getHandler(),
|
||||
context.getClass(),
|
||||
]);
|
||||
|
||||
if (!requiredRoles) {
|
||||
return true;
|
||||
}
|
||||
const { user } = context.switchToHttp().getRequest();
|
||||
return requiredRoles.some((role) => user.roles?.includes(role));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user