【Spring开发】 老板爱瞎改权限怎么办:注解+AOP 打造复杂权限体系

news/2025/1/6 21:57:28/文章来源:https://www.cnblogs.com/o-O-oO/p/18650839
引言一、权限控制的基本概念二、系统需求分析三、库表设计四、权限控制的注解设计五、切面实现六、权限控制的业务逻辑实现七、结论

引言

在软件开发的世界里,权限控制是确保数据安全和业务流程合规性的关键。然而,当老板或管理层频繁地调整权限设置时,这不仅会打乱开发节奏,还可能导致安全漏洞和性能问题。这种情况下,一个灵活、可扩展且易于管理的权限控制系统就显得尤为重要。

本文将探讨如何使用Java注解和Spring AOP(面向切面编程)来构建一个复杂而强大的权限控制体系。这个体系将能够应对频繁变更的权限需求,同时确保系统的安全性和稳定性。

在这个体系中,我们将通过注解来声明权限需求,利用AOP来实现权限检查的逻辑。这种方法不仅可以减少代码侵入性,还能提高系统的可维护性和可扩展性。我们将从权限控制的基本概念出发,逐步深入到系统需求分析、数据库设计、注解定义、切面实现,以及业务逻辑的实现。

一、权限控制的基本概念

权限控制是软件系统安全性的核心组成部分,它确保了只有授权的用户才能访问特定的资源或执行特定的操作。在企业级应用中,权限控制的复杂性随着业务需求的增加而增加,这要求系统能够灵活地处理不同层次和类型的权限。

定义组织权限和个人权限

组织权限通常指的是基于用户所属组织结构的权限。这种权限控制方式允许系统根据用户所在的部门、团队或公司层级来限制或授权访问。例如,一个公司的财务部门可能只能访问与财务相关的数据和功能,而不能访问人力资源或研发部门的数据。

个人权限则是指分配给特定用户的权限,与用户所属的组织结构无关。这种权限控制方式允许系统为每个用户定制访问控制,以满足个性化的需求。例如,一个销售代表可能被授权访问客户数据,但仅限于他们自己的客户记录。

权限的层次结构和作用域

权限控制系统通常采用层次结构来组织权限,以便于管理和维护。在这种结构中,权限可以被分为不同的级别,每个级别对应不同的访问控制需求。例如,一个简单的权限层次结构可能包括“只读”、“编辑”和“管理”三个级别。

权限的作用域决定了权限的适用范围。在某些系统中,权限可能仅适用于特定的资源或操作,而在其他系统中,权限可能跨越多个资源或操作。例如,一个用户可能被授权访问特定的数据库表,但仅限于查询操作,而不能进行数据的插入或删除。

权限与角色的关系

在权限控制系统中,角色是将权限分配给用户的一个有效方式。通过为不同的角色分配不同的权限集合,系统可以简化权限管理,并提高灵活性。例如,一个“管理员”角色可能拥有访问和修改所有资源的权限,而一个“普通用户”角色可能只有访问特定资源的权限。

角色和权限之间的关系通常是多对多的。一个用户可以拥有多个角色,而一个角色也可以包含多个权限。这种关系允许系统灵活地为用户分配权限,同时也方便了权限的更新和维护。

在复杂的系统中,角色和权限的关系可能更加复杂。例如,可能存在继承关系,其中某些角色继承了其他角色的权限,或者存在特定的规则来限制角色的权限组合。

二、系统需求分析

在设计一个复杂的权限控制系统时,需求分析是至关重要的第一步。它确保我们能够准确理解业务需求,并设计出一个既能满足当前需求又能适应未来变化的系统。以下是对复杂权限控制需求的描述,以及确定的业务规则。

2.1 描述复杂的权限控制需求

1、组织权限

组织权限要求系统能够根据用户所属的组织结构来授权或限制对特定功能的访问。例如,一个组织可能有不同的部门,每个部门可能有不同的权限集。
2、个人权限

个人权限要求系统能够为每个用户设置特定的权限,这些权限可能与用户的角色或组织结构无关。例如,某些用户可能被赋予访问敏感数据的权限。
3、数量限制

数量限制要求系统能够跟踪和限制用户或组织可以执行的特定操作的数量。例如,一个用户可能在一个时间段内只能提交一定数量的请求。
4、特殊角色权限

特殊角色权限要求系统能够识别并授权某些特殊角色,这些角色可能拥有超越常规权限控制的权限。例如,系统管理员可能可以访问系统的所有部分,不受常规权限限制。

2.2 确定权限控制的业务规则

权限继承: 用户继承其所属组织和角色的权限。

权限覆盖: 个人权限可以覆盖组织权限。

数量限制: 用户的权限可能受到数量限制,一旦达到限制,即使有权限也无法执行操作。

角色优先级: 特殊角色可能具有高优先级,可以覆盖其他所有权限设置。

权限审计: 所有权限的授予和使用都应该被记录和审计,以确保系统的安全性和合规性。

2.3 流程图

下面是一个简化的流程图,展示了如何通过注解和切面来实现上述复杂的权限控制需求:

在这个架构中,用户请求首先到达Spring Security Filter,然后进入权限检查环节。权限检查包括特殊角色权限检查、数量限制检查、组织权限检查和个人权限检查。如果用户通过所有检查,则可以访问业务逻辑处理;如果任何一个检查失败,请求将被拒绝。

三、库表设计

在设计一个复杂的权限控制系统时,数据库的设计是核心部分,它需要能够支持组织权限、个人权限、数量限制以及特殊角色权限等功能。以下是针对这些需求设计的数据库表结构。

组织表(Organizations)

字段名 数据类型 描述
organization_id INT 主键,自增
name VARCHAR 组织名称
parent_id INT 父组织ID,用于层级关系
description TEXT 组织描述

用户表(Users)

字段名 数据类型 描述
user_id INT 主键,自增
username VARCHAR 用户名
password_hash VARCHAR 加密密码
organization_id INT 所属组织ID
email VARCHAR 电子邮件
created_at DATETIME 创建时间
updated_at DATETIME 更新时间

角色表(Roles)

字段名 数据类型 描述
role_id INT 主键,自增
name VARCHAR 角色名称
description TEXT 角色描述

权限表(Permissions)

字段名 数据类型 描述
permission_id INT 主键,自增
name VARCHAR 权限名称
description TEXT 权限描述

角色权限关联表(Role_Permissions)

字段名 数据类型 描述
role_id INT 角色ID,外键
permission_id INT 权限ID,外键

用户角色关联表(User_Roles)

字段名 数据类型 描述
user_id INT 用户ID,外键
role_id INT 角色ID,外键

组织权限关联表(Organization_Permissions)

字段名 数据类型 描述
organization_id INT 组织ID,外键
permission_id INT 权限ID,外键
limit INT 允许的操作数量限制

数据库设计说明

组织表: 存储组织的基本信息,包括层级关系。

用户表: 存储用户的基本信息,包括所属组织的ID。

角色表: 存储角色的基本信息。

权限表: 存储权限的基本信息。

角色权限关联表: 定义角色和权限之间的关系,一个角色可以有多个权限,一个权限可以被多个角色拥有。

用户角色关联表: 定义用户和角色之间的关系,一个用户可以有多个角色。

组织权限关联表: 定义组织和权限之间的关系,并设置操作数量限制。

这种设计允许系统灵活地管理复杂的权限控制需求,包括组织权限、个人权限、数量限制和特殊角色权限。通过外键关联,可以确保数据的一致性和完整性。在实际应用中,可能还需要根据具体需求添加更多的字段或表,例如,用于跟踪权限使用情况的审计日志表。

请注意,这只是一个基本的设计,实际的数据库设计可能需要根据具体的业务需求和性能要求进行调整。在实现时,还需要考虑索引优化、数据一致性、备份和恢复策略等因素。

四、权限控制的注解设计

在Java中,注解(Annotation)是一种特殊的接口,用于为代码提供元数据。在权限控制中,注解可以被用来标记需要进行权限检查的方法或类。以下是为权限控制设计的三个注解:@PermissionRequired、@RoleRequired 和 @LimitRequired。
定义权限注解(@PermissionRequired):

@PermissionRequired 注解用于标记需要特定权限才能访问的方法。它接受一个或多个权限名称作为参数,表示用户必须拥有这些权限之一才能执行该方法。

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PermissionRequired {String[] value();
}

定义角色注解(@RoleRequired):

@RoleRequired 注解用于标记需要特定角色才能访问的方法。它接受一个或多个角色名称作为参数,表示用户必须拥有这些角色之一才能执行该方法。

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface RoleRequired {String[] value();
}

定义数量限制注解(@LimitRequired):

@LimitRequired 注解用于标记需要检查数量限制的方法。它接受一个参数,表示该方法对应的操作类型,这个操作类型将用于检查用户或组织是否达到了操作数量的限制。

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface LimitRequired {String value();
}

使用示例

以下是如何使用这些注解的示例:

@RestController
@RequestMapping("/api")
public class ApiController {@PermissionRequired("READ_DATA")@GetMapping("/data")public ResponseEntity<?> getData() {// 方法逻辑}@RoleRequired({"ADMIN", "MANAGER"})@PostMapping("/config")public ResponseEntity<?> updateConfig() {// 方法逻辑}@LimitRequired("REQUEST_LIMIT")@GetMapping("/request")public ResponseEntity<?> makeRequest() {// 方法逻辑}
}

在这个示例中,getData 方法需要用户拥有 READ_DATA 权限,updateConfig 方法需要用户拥有 ADMIN 或 MANAGER 角色,而 makeRequest 方法需要检查 REQUEST_LIMIT 类型的数量限制。

权限注解的实现细节

这些注解的实现将依赖于Spring框架的AOP(面向切面编程)功能。通过创建相应的切面,我们可以在方法执行前拦截这些注解,并执行权限检查逻辑。如果用户没有通过检查,系统将阻止方法的执行并返回相应的错误信息。

五、 切面实现

在Spring框架中,AOP(面向切面编程)是一种编程范式,它允许我们在不修改业务逻辑代码的情况下,对横切关注点(如日志记录、事务管理、权限检查等)进行模块化。通过使用AOP,我们可以将权限检查逻辑从业务逻辑中分离出来,提高代码的可维护性和重用性。

介绍Spring AOP的基本概念

Spring AOP基于代理机制,它允许我们在不改变现有代码结构的情况下,通过声明额外的逻辑来扩展程序的行为。
在Spring AOP中,主要涉及以下几个核心概念:

切面(Aspect): 切面是封装横切关注点的类,比如日志、事务、权限控制等。

连接点(Join point): 连接点是程序执行过程中的一个点,如方法的调用或异常的抛出。在Spring AOP中,连接点总是方法的执行。

通知(Advice): 通知是切面在某连接点上的执行动作,它定义了在何时何地执行哪些逻辑。常见的通知类型包括:前置通知(Before)、后置通知(After)、返回通知(AfterReturning)、异常通知(AfterThrowing)和环绕通知(Around)。

切点(Pointcut): 切点是一组匹配连接点的规则,它定义了哪些连接点会被切面所拦截。

目标对象(Target Object): 目标对象是指被代理的对象,即包含连接点的对象。

代理(Proxy): 代理是AOP框架创建的,它包裹了目标对象,并在执行连接点之前或之后执行通知。

定义权限检查切面(PermissionAspect)

PermissionAspect 是一个切面类,它使用Spring AOP来拦截被权限注解标记的方法,并执行权限检查逻辑。

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;@Aspect
@Component
public class PermissionAspect {@Pointcut("@annotation(permissionRequired)")public void permissionPointcut(PermissionRequired permissionRequired) {}@Before("permissionPointcut(permissionRequired)")public void checkPermission(JoinPoint joinPoint, PermissionRequired permissionRequired) {// 获取当前用户User currentUser = SecurityContextHolder.getContext().getAuthentication().getPrincipal();// 检查用户是否拥有所需的权限for (String permission : permissionRequired.value()) {if (!permissionService.hasPermission(currentUser, permission)) {throw new AccessDeniedException("Access Denied: No permission to perform this operation.");}}}// 其他方法,如角色检查、数量限制检查等
}

组织权限检查

组织权限检查需要查询数据库,确定当前用户所属的组织是否拥有特定的权限。

public boolean hasOrganizationPermission(User currentUser, String permission) {// 查询数据库,检查用户所属组织是否有权限
}

个人权限检查

个人权限检查需要查询数据库,确定当前用户是否拥有特定的权限。

public boolean hasUserPermission(User currentUser, String permission) {// 查询数据库,检查用户是否有权限
}

数量限制检查

数量限制检查需要查询数据库,确定当前用户或组织是否达到了操作数量的限制。

public boolean isLimitExceeded(User currentUser, String limitType) {// 查询数据库,检查是否达到数量限制
}

特殊角色检查

特殊角色检查需要查询数据库,确定当前用户是否拥有特殊角色。

public boolean hasSpecialRole(User currentUser) {// 查询数据库,检查用户是否拥有特殊角色
}

综合检查逻辑

在实际的权限检查逻辑中,我们需要综合考虑组织权限、个人权限、数量限制和特殊角色。这可能涉及到多个方法的调用和复杂的逻辑判断。

在PermissionAspect中,我们可以使用环绕通知(Around Advice)来实现这一逻辑,确保在方法执行前后进行必要的检查。

六、权限控制的业务逻辑实现

在权限控制系统中,业务逻辑层是实现权限检查功能的核心。它负责与数据库交互,获取用户、角色和权限数据,并根据这些数据执行权限检查。以下是三个关键服务的实现:UserService、RoleService 和 PermissionService。

用户权限服务(UserService)

UserService 提供了与用户相关的业务逻辑,包括获取用户信息、用户权限以及用户所属的角色。

@Service
public class UserService {@Autowiredprivate UserRepository userRepository;public User getUserById(Long userId) {return userRepository.findById(userId).orElse(null);}public Set<String> getUserPermissions(Long userId) {User user = getUserById(userId);if (user != null) {Set<Role> roles = user.getRoles();Set<String> permissions = new HashSet<>();for (Role role : roles) {permissions.addAll(role.getPermissions());}return permissions;}return Collections.emptySet();}// 其他用户相关业务逻辑
}

角色权限服务(RoleService)

RoleService 提供了与角色相关的业务逻辑,包括获取角色信息、角色权限以及分配给角色的权限。

@Service
public class RoleService {@Autowiredprivate RoleRepository roleRepository;public Role getRoleById(Long roleId) {return roleRepository.findById(roleId).orElse(null);}public Set<String> getRolePermissions(Long roleId) {Role role = getRoleById(roleId);if (role != null) {return role.getPermissions();}return Collections.emptySet();}// 其他角色相关业务逻辑
}

权限检查服务(PermissionService)

PermissionService 是权限控制系统的核心,它负责检查用户是否拥有特定的权限、角色以及是否超出操作数量限制。

@Service
public class PermissionService {@Autowiredprivate UserService userService;@Autowiredprivate RoleService roleService;public boolean hasPermission(User user, String permission) {if (user == null) {return false;}Set<String> userPermissions = userService.getUserPermissions(user.getId());return userPermissions.contains(permission);}public boolean hasRole(User user, String role) {if (user == null) {return false;}Set<Role> userRoles = user.getRoles();for (Role userRole : userRoles) {if (userRole.getName().equals(role)) {return true;}}return false;}public boolean isLimitExceeded(User user, String limitType) {// 实现检查用户或组织是否超出了操作数量限制// 这可能涉及查询数据库中的计数器或使用缓存return false;}// 特殊角色检查public boolean hasSpecialRole(User user) {// 实现检查用户是否拥有特殊角色return hasRole(user, "ADMIN"); // 示例:假设"ADMIN"是特殊角色}
}

业务逻辑实现说明

在这些服务中,我们使用了Spring框架的@Service注解来标记服务类,并使用@Autowired注解来自动注入所需的数据访问对象(如UserRepository 和 RoleRepository)。这些服务提供了与数据库交互的方法,以及基于这些数据执行权限检查的逻辑。

UserService 和 RoleService 提供了获取用户和角色相关信息的方法,而PermissionService 则使用这些信息来执行实际的权限检查。这些服务可以被AOP切面或其他业务逻辑组件调用,以确保在执行敏感操作之前进行适当的权限验证。

请注意,这些服务的实现可能需要根据具体的业务需求和数据库设计进行调整。在实际应用中,还可能需要考虑性能优化、缓存策略和安全性等因素。

七、结论

在本文中,我们深入探讨了如何使用Java注解和Spring AOP来实现一个复杂而灵活的权限控制系统。我们从权限控制的基本概念出发,分析了系统需求,并设计了相应的数据库结构。

接着,我们定义了权限注解,并实现了一个切面来拦截和检查这些注解。最后,我们实现了业务逻辑层,提供了用户权限服务、角色权限服务和权限检查服务。

总结

权限控制的基本概念: 我们定义了组织权限、个人权限,并讨论了权限的层次结构和作用域,以及权限与角色的关系。

系统需求分析: 我们描述了复杂的权限控制需求,包括组织权限、个人权限、数量限制和特殊角色权限,并确定了业务规则。

库表设计: 我们设计了数据库表结构,包括组织表、用户表、角色表、权限表以及相关的关联表。

权限控制的注解设计: 我们定义了三个注解:@PermissionRequired、@RoleRequired 和 @LimitRequired,用于标记需要权限检查的方法。

切面实现: 我们介绍了Spring AOP的基本概念,并实现了一个权限检查切面,包括组织权限检查、个人权限检查、数量限制检查和特殊角色检查。

权限控制的业务逻辑实现: 我们实现了用户权限服务、角色权限服务和权限检查服务,这些服务提供了与数据库交互和执行权限检查的逻辑。

实现的优缺点

优点

灵活性: 使用注解和AOP,我们可以灵活地在不同的方法上应用权限控制,而不需要修改业务逻辑代码。

可维护性: 将权限控制逻辑从业务逻辑中分离出来,使得代码更容易维护和扩展。

重用性: 定义的注解和切面可以在多个项目中重用,减少了重复代码。

安全性: 通过细致的权限控制,可以确保系统的安全性,防止未授权访问。

缺点

复杂性: 实现一个复杂的权限控制系统可能会增加系统的复杂性,特别是在设计和维护数据库结构和业务逻辑时。

性能考虑: 权限检查可能会引入额外的性能开销,特别是在涉及大量数据库查询的情况下。

安全性依赖: 系统的安全性高度依赖于权限控制的实现,任何实现上的漏洞都可能导致安全风险。

未来可能的改进方向

缓存策略: 为了提高性能,可以引入缓存策略,减少对数据库的直接查询。

动态权限更新: 支持动态更新权限设置,而不需要重新部署或重启应用程序。

细粒度权限控制: 实现更细粒度的权限控制,例如基于属性的访问控制(ABAC)。

审计和监控: 增加审计和监控功能,记录所有权限检查的操作,以便进行安全分析和问题排查。

用户界面: 开发一个用户友好的管理界面,使管理员能够轻松地管理用户、角色和权限。

多因素认证: 结合多因素认证(MFA)增强系统的安全性。

通过这些改进,我们可以进一步提高权限控制系统的性能、安全性和用户体验。随着技术的发展和业务需求的变化,权限控制系统也需要不断地演进和优化。

原创 我是程序汪

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/863554.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

01.03 CW 模拟赛 T2. game

思路 先把赛时的思路搬一下你发现确定两个人的起始点, 其实是可以确定 \(\rm{Alice}\) 的选点可能的, 考虑写个代码验证一下 具体的, 就是分成两个弧, \(\rm{Alice}\) 可以选择一个弧的优势(过半), 然后其他的劣势 感觉现在是猜结论, 全靠感性, 我也不知道怎么解释这个问题 那么…

1.3 可压缩流:喷管和机翼

1.3 可压缩流:喷管和机翼 前言 劳张、劳江必看,劳高请随缘。 超音速喷嘴 基本 对于等熵流动,给出速度变化和管道截面面积变化的关系式: \[\begin{equation}(M^2-1)\frac{dV}{V}=\frac{dA}{A} \end{equation} \]对该公式分3种情况讨论:低速、亚音速,M<1:此时\(M^2-1&l…

C++程序运行的三种方式

1、源代码的注释,2、c++运行方式一、例程 编一个程序,计算机随机产生一个整数(1至5),自己输入一个整数,若两数相同,则输出“恭喜你,中奖了!奖金10元”,否则输出“没中奖,请付费2元”;同时公布中奖号码。 #include <iostream> #include <cstdlib> …

25.01.03

-1喜欢我 \(O(n^2 \log^2 n)\) 过 \(2e5\) 吗😋_ _ _ _ _ _ Ciallo~(∠・ω< )⌒★A 有解的一个必要不充分条件是: 设 \(L_i\) 表示 \(l \le i\) 的左端点数量,\(R_i\) 表示 \(r < i\) 的右端点数量,对于每个位置 \(i\) 满足 \(L_i \ge i \wedge R_i < i\)。 意…

MES 包装 称重 测试备忘

称的功能都差不多,需要MES适配不同品牌的称。 串口输出2中模式:可以调节 精度:精度0.1g 精度是0.001kg 第一种(默认):不停的串口输出。 输出速度很快,容易导致程序死掉。 第二种模式:重量稳定输出。 下面是重量稳定输出模式截图: ------ erwa.cn 二娃测试备忘

深度学习基础理论————训练加速(单/半/混合精度训练)/显存优化(gradient-checkpoint)

主要介绍单精度/半精度/混合精度训练,以及部分框架(DeepSpeed/Apex) 不同精度训练 单精度训练(single-precision)指的是用32位浮点数(FP32)表示所有的参数、激活值和梯度 半精度训练(half-precision)指的是用16位浮点数(FP16 或 BF16)表示数据。(FP16 是 IEEE 标准…

使用学生优惠创建 Azure Database for MySQL 数据库

文章首先强调了需要一个已通过学生认证的 Azure 账户,然后详细讲解了从登录 Azure 门户页面、选择免费服务、配置服务器和网络等步骤,最终成功创建并部署 Azure Database for MySQL。前言 在此之前,你需要拥有一个已通过学生认证的 Azure 账户。关于通过 Azure 学生认证,网…

【Windows】修改虚拟内存位置

这篇文章详细介绍了如何在 Windows 系统中将虚拟内存文件(pagefile.sys)从 C 盘移动到其他盘。步骤包括查看文件位置、检查和关闭 C 盘加密、修改注册表、设置新的虚拟内存位置并重启电脑,最终实现文件转移。问题:系统优化中,希望将pagefile.sys文件(即虚拟内存)移动到其…

ex7.3

import numpy as np from scipy.interpolate import lagrange import matplotlib.pyplot as plt import matplotlib yx = lambda x: 1/(1+x**2) def fun(n): x = np.linspace(-5, 5, n+1) p = lagrange(x, yx(x)) # n次插值多项式 return p x0 = np.linspace(-5, 5, 100) plt…

【hashMap扩容】关于hashMap扩容以后,新下标的理解

首先我们知道hashMap在存取元素的时候的下标算法是这样子的 根据当前元素(e)的hash值((e.hashCode()) ^ (e.hashCode() >>> 16))去与上当前hashMap的容量减一(Cap-1) put和get都是如此 put get所以在扩容算法中,元素的坐标也应是用这种方式存的,看一下代码我们会发现…

【自动化测试基础】Pytest前后置处理

Pytest的前后置(固件、夹具)处理 有一些初始化配置和测试之后的收尾,只需要处理一次,这个时候我们就要用到夹具。 Pytest提供了以下几种setup和teardown方法:setup_function 和 teardown_function: 用于每个测试函数 setup_method 和 teardown_method: 用于每个测试方法(…

进阶大模型开发框架LangChain

本文来自博客园,作者:王竹笙,转载请注明原文链接:https://www.cnblogs.com/edeny/p/18650785