Vue3 + Nest 实现权限管理系统 后端篇(三):基于RBAC 权限控制实现

简介: RBAC(Role Based Access Control)是基于角色的权限控制,简单来说就是给用户赋予一些角色,那么该用户就会拥有这些角色的所有权限。接下来我们就用 NestJS 来实现基于 RBAC 的权限控制

RBAC简介

RBAC(Role Based Access Control)是基于角色的权限控制,简单来说就是给用户赋予一些角色,那么该用户就会拥有这些角色的所有权限。接下来我们就用 NestJS 来实现基于 RBAC 的权限控制

创建表

除了我们已有的 user 表,我们还需创建角色表(role),权限字段表(permission)。可以直接用命令新增两个模块

nest g res role
nest g res permission

进入权限 permission 模块的 entity 中编写 permission 表的字段

//permission.entity.ts
import {
   
   
  Column,
  CreateDateColumn,
  Entity,
  PrimaryGeneratedColumn,
  UpdateDateColumn,
} from 'typeorm';

@Entity()
export class Permission {
   
   
  @PrimaryGeneratedColumn()
  id: string;

  @Column({
   
   
    length: 50,
  })
  name: string;

  @Column({
   
   
    length: 100,
    nullable: true,
  })
  desc: string;

  @CreateDateColumn()
  createTime: Date;

  @UpdateDateColumn()
  updateTime: Date;
}

一个权限可以赋给多个角色,同时一个角色也可以有多个权限,因此它们是多对多(ManyToMany)的关系,所以我们可以用role_permission_relation关系表将它们关联起来,角色(role)模块下的 entity 如下

//role.entity.ts
import {
   
   
  Column,
  CreateDateColumn,
  Entity,
  JoinTable,
  ManyToMany,
  PrimaryGeneratedColumn,
  UpdateDateColumn,
} from 'typeorm';
import {
   
    Permission } from '../../permission/entities/permission.entity';
@Entity()
export class Role {
   
   
  @PrimaryGeneratedColumn()
  id: string;

  @Column({
   
   
    length: 20,
  })
  name: string;

  @CreateDateColumn()
  createTime: Date;

  @UpdateDateColumn()
  updateTime: Date;

  @ManyToMany(() => Permission)
  @JoinTable({
   
   
    name: 'role_permission_relation',
  })
  permissions: Permission[];
}

同样的用户(user)与角色(role)直接也是多对多的关系,因此 user 的 entity 修改如下

import {
   
   
  Column,
  Entity,
  PrimaryGeneratedColumn,
  BeforeInsert,
  ManyToMany,
  JoinTable,
} from 'typeorm';
import encry from '../../utils/crypto';
import * as crypto from 'crypto';
import {
   
    Role } from '../../role/entities/role.entity';
@Entity('user')
export class User {
   
   
  @PrimaryGeneratedColumn()
  id: string; // 标记为主键,值自动生成

  @Column({
   
    length: 30 })
  username: string; //用户名
  @Column({
   
    nullable: true })
  nickname: string; //昵称
  @Column()
  password: string; //密码
  @Column({
   
    nullable: true })
  avatar: string; //头像
  @Column({
   
    nullable: true })
  email: string; //邮箱

  @ManyToMany(() => Role)
  @JoinTable({
   
   
    name: 'user_role_relation',
  })
  roles: Role[]; //角色

  @Column({
   
    nullable: true })
  salt: string;
  @Column({
   
    type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' })
  create_time: Date;

  @Column({
   
    type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' })
  update_time: Date;
  @BeforeInsert()
  beforeInsert() {
   
   
    this.salt = crypto.randomBytes(4).toString('base64');
    this.password = encry(this.password, this.salt);
  }
}

最后在各自的 module 导入实体,以 permission.module.ts 为例

import {
   
    Module } from '@nestjs/common';
import {
   
    PermissionService } from './permission.service';
import {
   
    PermissionController } from './permission.controller';
import {
   
    Permission } from './entities/permission.entity';
import {
   
    TypeOrmModule } from '@nestjs/typeorm';
@Module({
   
   
  controllers: [PermissionController],
  providers: [PermissionService],
  imports: [TypeOrmModule.forFeature([Permission])],
})
export class PermissionModule {
   
   }

启动项目,我们就会看到数据库中多了几张表

image.png

添加模拟数据

接下来给每个模块添加一个新增数据的接口用于添加数据来观察它们之间的关系

permission 模块

新增 permission 数据很简单,直接保存前端传来的的namedesc字段即可

//permission.service.ts
import {
   
    Injectable } from '@nestjs/common';
import {
   
    InjectRepository } from '@nestjs/typeorm';
import {
   
    ApiErrorCode } from 'src/common/enums/api-error-code.enum';
import {
   
    ApiException } from 'src/common/filter/http-exception/api.exception';
import {
   
    Repository } from 'typeorm';
import {
   
    CreatePermissionDto } from './dto/create-permission.dto';
import {
   
    Permission } from './entities/permission.entity';
@Injectable()
export class PermissionService {
   
   
  constructor(
    @InjectRepository(Permission)
    private permissionRepository: Repository<Permission>,
  ) {
   
   }
  async create(createPermissionDto: CreatePermissionDto) {
   
   
    const name = createPermissionDto.name;
    const existPermission = await this.permissionRepository.findOne({
   
   
      where: {
   
    name },
    });

    if (existPermission)
      throw new ApiException('权限字段已存在', ApiErrorCode.PERMISSSION_EXIST);
    return await this.permissionRepository.save(createPermissionDto);
  }
}
//create-user.dto.ts
export class CreatePermissionDto {
   
   
  name: string;
  desc: string;
}

然后我们可以调用http://localhost:3000/permission接口新增一些权限字段create,read,update,delete

image.png

新增完毕之后,permission 表如下

image.png

role 模块

新增 role 表中的数据和上面有所不同,因为它有一个关系字段permissions,因此需要导入 permission 实体,然后根据前端传来的权限字段 id(permissionIds)查询对应 permission 实体插入 role 表中

//role.module.ts
import {
   
    Module } from '@nestjs/common';
import {
   
    RoleService } from './role.service';
import {
   
    RoleController } from './role.controller';
import {
   
    Role } from './entities/role.entity';
import {
   
    Permission } from '../permission/entities/permission.entity';
import {
   
    TypeOrmModule } from '@nestjs/typeorm';
@Module({
   
   
  controllers: [RoleController],
  providers: [RoleService],
  imports: [TypeOrmModule.forFeature([Role, Permission])],
})
export class RoleModule {
   
   }
//create-role.dto.ts
export class CreateRoleDto {
   
   
  name: string;
  permissionIds: number[];
}
//role.service.ts
import {
   
    Injectable } from '@nestjs/common';
import {
   
    InjectRepository } from '@nestjs/typeorm';
import {
   
    In, Repository } from 'typeorm';
import {
   
    CreateRoleDto } from './dto/create-role.dto';
import {
   
    Role } from './entities/role.entity';
import {
   
    Permission } from '../permission/entities/permission.entity';
import {
   
    ApiException } from 'src/common/filter/http-exception/api.exception';
import {
   
    ApiErrorCode } from 'src/common/enums/api-error-code.enum';
@Injectable()
export class RoleService {
   
   
  constructor(
    @InjectRepository(Role)
    private roleRepository: Repository<Role>,
    @InjectRepository(Permission)
    private permissionRepository: Repository<Permission>,
  ) {
   
   }
  async create(createRoleDto: CreateRoleDto) {
   
   
    //查询传入数组permissionIds的全部permission实体
    const permissions = await this.permissionRepository.find({
   
   
      where: {
   
   
        id: In(createRoleDto.permissionIds),
      },
    });
    const name = createRoleDto.name;
    const existRole = await this.roleRepository.findOne({
   
   
      where: {
   
    name },
    });

    if (existRole)
      throw new ApiException('角色已存在', ApiErrorCode.ROLE_EXIST);
    return this.roleRepository.save({
   
    permissions, name });
  }
}

最后调用http://localhost:3000/role就能添加一些角色,分别添加超级管理员(create,read,update,delete),管理员(create,read,update),用户(read)

image.png

添加完成之后 role 表如下

image.png

同时我们可以查看role_permission_relation关系表来看一下它们的多对多关系

image.png

user 模块

user 模块和 role 模块逻辑差不多,需要查询 role 表,因此要引入 role 实体

//user.module.ts
import {
   
    Module } from '@nestjs/common';
import {
   
    UserService } from './user.service';
import {
   
    UserController } from './user.controller';
import {
   
    User } from './entities/user.entity';
import {
   
    Role } from '../role/entities/role.entity';
import {
   
    TypeOrmModule } from '@nestjs/typeorm';
import {
   
    JwtModule } from '@nestjs/jwt';
@Module({
   
   
  controllers: [UserController],
  providers: [UserService],
  imports: [
    TypeOrmModule.forFeature([User, Role]),
    JwtModule.register({
   
    secret: process.env.JWT_SECRET }),
  ],
  exports: [UserService],
})
export class UserModule {
   
   }

在 user.service.ts 写对应逻辑

import {
   
    HttpException, HttpStatus, Injectable } from '@nestjs/common';
import {
   
    CreateUserDto } from './dto/create-user.dto';
import {
   
    User } from './entities/user.entity';
import {
   
    Role } from '../role/entities/role.entity';
import {
   
    InjectRepository } from '@nestjs/typeorm';
import {
   
    In, Repository } from 'typeorm';
import {
   
    ApiException } from 'src/common/filter/http-exception/api.exception';
import {
   
    ApiErrorCode } from 'src/common/enums/api-error-code.enum';

@Injectable()
export class UserService {
   
   
  constructor(
    @InjectRepository(User)
    private userRepository: Repository<User>,
    @InjectRepository(Role)
    private roleRepository: Repository<Role>,
  ) {
   
   }
  async create(createUserDto: CreateUserDto) {
   
   
    const {
   
    username, password, roleIds } = createUserDto;
    const existUser = await this.userRepository.findOne({
   
   
      where: {
   
    username },
    });

    if (existUser)
      throw new ApiException('用户已存在', ApiErrorCode.USER_EXIST);
    try {
   
   
      //查询数组roleIds对应所有role的实例
      const roles = await this.roleRepository.find({
   
   
        where: {
   
   
          id: In(roleIds),
        },
      });
      const newUser = await this.userRepository.create({
   
   
        username,
        password,
        roles,
      });
      await this.userRepository.save(newUser);
      return '注册成功';
    } catch (error) {
   
   
      throw new HttpException(error, HttpStatus.INTERNAL_SERVER_ERROR);
    }
  }
  async findOne(username: string) {
   
   
    const user = await this.userRepository.findOne({
   
   
      where: {
   
    username },
    });

    if (!user)
      throw new ApiException('用户名不存在', ApiErrorCode.USER_NOTEXIST);
    return user;
  }
}

在 create-user.dto.ts 规定前端传来参数格式

//create-user.dto.ts
export class CreateUserDto {
   
   
  username: string;
  password: string;
  roleIds: number[];
}

创建两个用户分别为 admin 和 user1 分别赋予全部角色和用户角色

image.png

权限控制

我们可以通过自定义装饰器在调用接口之前传入一个权限数组,即必须拥有数组内的权限才能够调用此接口。所以可与创建一个全局的导航守卫,里面查询到该用户的所有权限然后与自定义装饰器传入的权限进行对比即可

现在 public 目录下新增一个@Permissions装饰器

//public.decorator.ts
import {
   
    SetMetadata } from '@nestjs/common';

export const Public = () => SetMetadata('isPublic', true);

export const Permissions = (...permissions: string[]) =>
  SetMetadata('permissions', permissions);

然后在 permission 模块下创建一个导航守卫

nest g guard permission --no-spec --flat

同样的,这个守卫需要设置为全局守卫

//permission.module.ts
import {
   
    Module } from '@nestjs/common';
import {
   
    PermissionService } from './permission.service';
import {
   
    PermissionController } from './permission.controller';
import {
   
    Permission } from './entities/permission.entity';
import {
   
    TypeOrmModule } from '@nestjs/typeorm';
import {
   
    APP_GUARD } from '@nestjs/core';
import {
   
    PermissionGuard } from '../permission/permission.guard';
@Module({
   
   
  controllers: [PermissionController],
  providers: [
    PermissionService,
    {
   
   
      provide: APP_GUARD,
      useClass: PermissionGuard,
    },
  ],
  imports: [TypeOrmModule.forFeature([Permission])],
})
export class PermissionModule {
   
   }

接下来我们开始写相关逻辑,在 user 中简单写一个测试接口,同时用我们刚写的自定义装饰器传入两个权限

//user.controller.ts
  @Post('test')
  @Permissions('create', 'read')
  test(@Body() testParams) {
   
   
    return this.userService.test(testParams);
  }

修改一下权限守卫,获取传入的元数据


//permission.guard.ts
import {
   
    CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import {
   
    Reflector } from '@nestjs/core';
import {
   
    Observable } from 'rxjs';

@Injectable()
export class PermissionGuard implements CanActivate {
   
   
  constructor(private reflector: Reflector) {
   
   }
  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
   
   
    const requiredPermissions = this.reflector.getAllAndOverride<string[]>(
      'permissions',
      [context.getClass(), context.getHandler()],
    );

    console.log(requiredPermissions);
    return true;
  }
}

在调用接口之前我们先把登录守卫修改一下,让其都放行,不然接口是调不通的,只需要修改auth.guard.ts让其都返回 true 即可

import {
   
   
  CanActivate,
  ExecutionContext,
  HttpException,
  HttpStatus,
  Injectable,
} from '@nestjs/common';
import {
   
    Request } from 'express';
import {
   
    JwtService } from '@nestjs/jwt';
import {
   
    ConfigService } from '@nestjs/config';
import {
   
    Reflector } from '@nestjs/core';
import {
   
    ApiErrorCode } from 'src/common/enums/api-error-code.enum';
import {
   
    ApiException } from 'src/common/filter/http-exception/api.exception';
@Injectable()
export class AuthGuard implements CanActivate {
   
   
  constructor(
    private jwtService: JwtService,
    private configService: ConfigService,
    private reflector: Reflector,
  ) {
   
   }
  async canActivate(context: ExecutionContext): Promise<boolean> {
   
   
    const isPublic = this.reflector.getAllAndOverride<boolean>('isPublic', [
      //即将调用的方法
      context.getHandler(),
      //controller类型
      context.getClass(),
    ]);



    //这里暂时都放行
    return true;



    if (isPublic) {
   
   
      return true;
    }
    const request = context.switchToHttp().getRequest();
    const token = this.extractTokenFromHeader(request);
    if (!token) throw new ApiException('验证不通过', ApiErrorCode.FORBIDDEN);
    try {
   
   
      const payload = await this.jwtService.verifyAsync(token, {
   
   
        secret: this.configService.get('JWT_SECRET'),
      });
      request['user'] = payload;
    } catch {
   
   
      throw new ApiException(
        '登录状态已过期,请重新登录',
        ApiErrorCode.LOGIN_EXPIRE,
      );
    }

    return true;
  }
  private extractTokenFromHeader(request: Request): string | undefined {
   
   
    const [type, token] = request.headers.authorization?.split(' ') ?? [];
    return type === 'Bearer' ? token : undefined;
  }
}

调用 user/test 接口查看我们的打印结果

image.png

可以发现已经获取到装饰器传来的权限数组了,接下来我们要做的是查询用户的所有权限与之对比,看是否包含装饰器传来的数组,包含的话则放行否则抛出没有权限的错误给前端。

因为需要查询该用户的权限字段,所以在user.service.ts写一个查询权限字段名的方法,这里模拟查询admin用户的权限

//user.service.ts
  async findPermissionNames() {
   
   
    const user = await this.userRepository.findOne({
   
   
      where: {
   
    username: 'admin' },
      relations: ['roles', 'roles.permissions'],
    });
    if (user) {
   
   
      const permissions = user.roles.flatMap((role) => role.permissions);
      const permissionNames = permissions.map((item) => item.name);

      return [...new Set(permissionNames)];
    } else {
   
   
      return [];
    }
  }

因为这个方法要提供给权限守卫使用,所以需要暴露出去,并在permission.module.ts中导入

//...
import {
   
    UserModule } from 'src/user/user.module';
@Module({
   
   
  //...
  imports: [UserModule, TypeOrmModule.forFeature([Permission])],
})
export class PermissionModule {
   
   }

然后在导航守卫中引入,因为获取的是异步的,因此守卫做了一点修改

//permission.guard.ts
import {
   
    CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import {
   
    Reflector } from '@nestjs/core';
import {
   
    UserService } from 'src/user/user.service';
@Injectable()
export class PermissionGuard implements CanActivate {
   
   
  constructor(
    private reflector: Reflector,
    private userServicese: UserService,
  ) {
   
   }
  async canActivate(context: ExecutionContext): Promise<boolean> {
   
   
    const requiredPermissions = this.reflector.getAllAndOverride<string[]>(
      'permissions',
      [context.getClass(), context.getHandler()],
    ) || [];
    const permissionNames = await this.userServicese.findPermissionNames();
    console.log(requiredPermissions);
    console.log(permissionNames);

    return true;
  }
}

调用user/test接口即可获得接口权限字段与该用户拥有的权限字段

image.png

最后看该角色权限是否包含接口权限即可

//permission.guard.ts
//...
const isContainedPermission = requiredPermissions.every((item) =>
  permissionNames.includes(item),
);
if (!isContainedPermission) {
   
   
  throw new ApiException('权限不足', ApiErrorCode.Forbidden);
}
return true;

完善一下

auth.guard.ts我们已经将 jwt 验证的信息放入了 request['user']中了

image.png

所以可以在权限守卫中获取到登录用户的信息,然后调用 user 模块中的findPermissionNames即可获取当前登录人的权限集合,权限守卫修改如下

//permission.guard.ts
import {
   
    CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import {
   
    Reflector } from '@nestjs/core';
import {
   
    UserService } from 'src/user/user.service';
import {
   
    Request } from 'express';
import {
   
    ApiException } from 'src/common/filter/http-exception/api.exception';
import {
   
    ApiErrorCode } from 'src/common/enums/api-error-code.enum';
@Injectable()
export class PermissionGuard implements CanActivate {
   
   
  constructor(
    private reflector: Reflector,
    private userServicese: UserService,
  ) {
   
   }
  async canActivate(context: ExecutionContext): Promise<boolean> {
   
   
    interface CusRequest extends Request {
   
   
      user?: any;
    }
    const request: CusRequest = context.switchToHttp().getRequest();
    const requiredPermissions =
      this.reflector.getAllAndOverride<string[]>('permissions', [
        context.getClass(),
        context.getHandler(),
      ]) || [];

    if (requiredPermissions.length === 0) return true;
    const [, token] = request.headers.authorization?.split(' ') ?? [];

    const permissionNames = await this.userServicese.findPermissionNames(
      token,
      request.user,
    );

    const isContainedPermission = requiredPermissions.every((item) =>
      permissionNames.includes(item),
    );
    if (!isContainedPermission) {
   
   
      throw new ApiException('权限不足', ApiErrorCode.Forbidden);
    }
    return true;
  }
}

findPermissionNames获取到传来的用户信息,将查询的 username 替换即可

//user.service.ts
  async findPermissionNames(token: string, userInfo) {
   
   
    const user = await this.userRepository.findOne({
   
   
      where: {
   
    username: userInfo.username },
      relations: ['roles', 'roles.permissions'],
    });
    if (user) {
   
   
      const permissions = user.roles.flatMap((role) => role.permissions);
      const permissionNames = permissions.map((item) => item.name);
      return [...new Set(permissionNames)];
    } else {
   
   
      return [];
    }
  }

最后调用登录接口登录只有一个用户角色的 user1 用户,拿到它的 token 调用user/test接口

image.png

因为角色用户只有read权限,而 test 接口需要create, read权限,所以被拦截了,我们将接口改成只需要read权限再试一下

//user.controller.ts
  @Post('test')
  @Permissions('read')
  test(@Body() testParams) {
   
   
    return this.userService.test(testParams);
  }

image.png

此时我们发现请求成功了!

到这里基于RBAC权限控制基本完成了,但是还有很多不足之处,比如不需要每次调用接口都查询数据库中用户的权限,我们只需要在登录时查询一次缓存在Redis中即可,这部分后续文章会进行介绍,欢迎点赞收藏加关注~

源码地址

Vue3 + Nest 实现权限管理系统 后端

相关文章
|
2月前
|
存储 前端开发 安全
实现“永久登录”:针对蜻蜓Q系统的用户体验优化方案(前端uni-app+后端Laravel详解)-优雅草卓伊凡
实现“永久登录”:针对蜻蜓Q系统的用户体验优化方案(前端uni-app+后端Laravel详解)-优雅草卓伊凡
174 5
|
6月前
|
JavaScript 前端开发 Java
制造业ERP源码,工厂ERP管理系统,前端框架:Vue,后端框架:SpringBoot
这是一套基于SpringBoot+Vue技术栈开发的ERP企业管理系统,采用Java语言与vscode工具。系统涵盖采购/销售、出入库、生产、品质管理等功能,整合客户与供应商数据,支持在线协同和业务全流程管控。同时提供主数据管理、权限控制、工作流审批、报表自定义及打印、在线报表开发和自定义表单功能,助力企业实现高效自动化管理,并通过UniAPP实现移动端支持,满足多场景应用需求。
585 1
|
4月前
|
小程序 安全 关系型数据库
专业打造一款圈子源码软件系统 / 后端 PHP 搭建部署一样实现利益化
本教程详解基于PHP后端与Uni-app的小程序开发全流程,涵盖技术选型、环境搭建、源码导入、接口对接及功能实现。采用Laravel/Symfony框架,结合MySQL/PostgreSQL数据库,使用WebSocket实现实时通信,并集成IM SDK实现音视频聊天。前端使用Uni-app开发,支持跨平台运行。教程包含完整部署流程与安全优化方案,助力快速搭建高性能、安全稳定的小程序系统。
310 5
|
6月前
|
存储 消息中间件 前端开发
PHP后端与uni-app前端协同的校园圈子系统:校园社交场景的跨端开发实践
校园圈子系统校园论坛小程序采用uni-app前端框架,支持多端运行,结合PHP后端(如ThinkPHP/Laravel),实现用户认证、社交关系管理、动态发布与实时聊天功能。前端通过组件化开发和uni.request与后端交互,后端提供RESTful API处理业务逻辑并存储数据于MySQL。同时引入Redis缓存热点数据,RabbitMQ处理异步任务,优化系统性能。核心功能包括JWT身份验证、好友系统、WebSocket实时聊天及活动管理,确保高效稳定的用户体验。
396 4
PHP后端与uni-app前端协同的校园圈子系统:校园社交场景的跨端开发实践
|
5月前
|
SQL 中间件 Go
开箱即用的GO后台管理系统 Kratos Admin - 后端项目结构说明
Kratos Admin 是一个开箱即用的 Go 语言后台管理系统,采用 Kratos 框架构建,提供清晰的项目结构与模块化设计。目录包含 API 定义、服务代码、配置文件、数据库初始化脚本及部署工具,支持 Docker 部署与自动化构建,便于快速开发和维护企业级应用。
196 0
|
5月前
|
JSON Cloud Native Go
开箱即用的GO后台管理系统 Kratos Admin - 后端权限控制
后端的权限控制主要分为两种: API权限控制; 数据权限控制。 在本文,我们不讨论数据权限的控制,主要讲API的权限控制。
359 0
|
5月前
|
自然语言处理 JavaScript 前端开发
一夜获千星!已获 1.7k+,Art Design Pro:Vue3 + Vite + TypeScript 打造的高颜值管理系统模板,这个让后端小哥直呼救命的后台系统
Art Design Pro 是一款基于 Vue 3、Vite 和 TypeScript 的高颜值后台管理系统模板,已获 1.7k+ 星标。项目专注于用户体验与视觉设计,支持主题切换、多语言、权限管理及图表展示等功能,内置常用业务组件,便于快速搭建现代化管理界面。其技术栈先进,开发体验流畅,适配多设备,满足企业级应用需求。项目地址:[GitHub](https://github.com/Daymychen/art-design-pro)。
860 11
|
6月前
|
开发框架 Java 关系型数据库
在Linux系统中安装JDK、Tomcat、MySQL以及部署J2EE后端接口
校验时,浏览器输入:http://[your_server_IP]:8080/myapp。如果你看到你的应用的欢迎页面,恭喜你,一切都已就绪。
494 17
|
4月前
|
人工智能 Java API
后端开发必看:零代码实现存量服务改造成MCP服务
本文介绍如何通过 **Nacos** 和 **Higress** 实现存量 Spring Boot 服务的零代码改造,使其支持 MCP 协议,供 AI Agent 调用。全程无需修改业务代码,仅通过配置完成服务注册、协议转换与工具映射,显著降低改造成本,提升服务的可集成性与智能化能力。
1180 1
|
4月前
|
前端开发 Java 数据库连接
后端开发中的错误处理实践:原则与实战
在后端开发中,错误处理是保障系统稳定性的关键。本文介绍了错误分类、响应设计、统一处理机制及日志追踪等实践方法,帮助开发者提升系统的可维护性与排障效率,做到防患于未然。

热门文章

最新文章