一文上手SpringSecurity【八】

RBAC(Role-Based Access Control),基于角色的访问控制。通过用户关联角色,角色关联权限,来间接的为用户赋予权限。

一、RBAC介绍

RBAC(Role-Based Access Control),即基于角色的访问控制模型。

1.1 基本概念

  • 角色(Role):
    • 角色是 RBAC 模型的核心概念之一。它是一组权限的集合,代表了特定的工作职责或功能。例如,可以有 “管理员”“普通用户”“财务人员” 等不同的角色。
    • 角色将用户与权限进行解耦,使得权限管理更加清晰和易于维护。
  • 用户(User)
    • 用户是系统的使用者。在 RBAC 模型中,用户通过被分配到不同的角色来获得相应的权限。
    • 一个用户可以被分配多个角色,从而拥有多个角色所对应的权限。
  • 权限(Permission)
    • 权限定义了对系统资源的具体操作许可。例如,可以是对某个文件的读取权限、对数据库表的修改权限等。
    • 权限通常与系统中的具体资源和操作相关联。

1.2 基本工作原理

  • 用户分配角色:

    • 系统管理员将不同的角色分配给用户。用户在登录系统后,根据其被分配的角色来确定拥有的权限。
    • 例如,新员工入职时,管理员可以根据其工作职责为其分配 “普通员工” 角色。
  • 角色关联权限:

    • 管理员将各种权限分配给不同的角色。每个角色拥有一组特定的权限集合。
    • 比如,“管理员” 角色可能拥有对系统所有资源的读写和管理权限,而 “普通用户” 角色可能只有对部分资源的读取权限。
  • 权限控制:

    • 当用户在系统中进行操作时,系统会根据用户的角色来判断其是否具有执行该操作的权限。
    • 如果用户具有相应的权限,则操作被允许;否则,操作被拒绝。

1.3 数据库表

1

create database if not exists `rj-security-db`;
use `rj-security-db`;-- 用户表
DROP TABLE IF EXISTS `tb_sys_user`;
CREATE TABLE `tb_sys_user`  (`id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',`name` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '用户名',`nick_name` varchar(150) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '昵称',`password` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '密码',`create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',PRIMARY KEY (`id`) USING BTREE,UNIQUE INDEX `name`(`name` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '用户管理' ROW_FORMAT = DYNAMIC;
INSERT INTO `tb_sys_user` VALUES (4, 'zhangsan', '张三', '$2a$10$dCr8Skk7kLa2kNCms.23aeiYI2RS2vrdoSae6Jz3.0w.YCiu9lmT2', NULL);
INSERT INTO `tb_sys_user` VALUES (5, 'jack', '杰克', '$2a$10$dCr8Skk7kLa2kNCms.23aeiYI2RS2vrdoSae6Jz3.0w.YCiu9lmT2', NULL);-- 角色表
DROP TABLE IF EXISTS `tb_sys_role`;
CREATE TABLE `tb_sys_role`  (`id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',`name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '角色名称',`create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 10 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '角色管理' ROW_FORMAT = DYNAMIC;INSERT INTO `tb_sys_role` VALUES (10, '超级管理员', NULL);
INSERT INTO `tb_sys_role` VALUES (11, '技术经理', NULL);
INSERT INTO `tb_sys_role` VALUES (12, '财务总监', NULL);
INSERT INTO `tb_sys_role` VALUES (13, '研发工程师', NULL);
INSERT INTO `tb_sys_role` VALUES (14, '人事专员', NULL);
INSERT INTO `tb_sys_role` VALUES (15, '产品经理', NULL);-- 权限表【菜单表】
DROP TABLE IF EXISTS `tb_sys_menu`;
CREATE TABLE `tb_sys_menu`  (`id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',`name` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '菜单名称',`parent_id` bigint NULL DEFAULT NULL COMMENT '父菜单ID,一级菜单为0',`url` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '菜单URL,类型:1.普通页面(如用户管理, /sys/user)2.嵌套完整外部页面,以http(s)开头的链接 ',`perms` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '授权(多个用逗号分隔,如:sys:user:add,sys:user:edit)',`type` int NULL DEFAULT NULL COMMENT '类型   0:目录   1:菜单   2:按钮',`icon` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '菜单图标',`order_num` int NULL DEFAULT NULL COMMENT '排序',`create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 64 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '菜单管理' ROW_FORMAT = DYNAMIC;INSERT INTO `tb_sys_menu` VALUES (64, '用户管理', 0, '/user/list', 'sys:user:list', NULL, NULL, NULL, NULL);
INSERT INTO `tb_sys_menu` VALUES (65, '用户添加', 64, '/user/add', 'sys:user:add', NULL, NULL, NULL, NULL);
INSERT INTO `tb_sys_menu` VALUES (66, '用户删除', 64, '/user/delete', 'sys:user:delete', NULL, NULL, NULL, NULL);
INSERT INTO `tb_sys_menu` VALUES (67, '用户更新', 64, '/user/update', 'sys:user:update', NULL, NULL, NULL, NULL);
INSERT INTO `tb_sys_menu` VALUES (68, '商品管理', 0, '/goods/list', 'sys:goods:list', NULL, NULL, NULL, NULL);
INSERT INTO `tb_sys_menu` VALUES (69, '商品添加', 68, '/goods/add', 'sys:goods:add', NULL, NULL, NULL, NULL);
INSERT INTO `tb_sys_menu` VALUES (70, '商品删除', 68, '/goods/delete', 'sys:goods:delete', NULL, NULL, NULL, NULL);
INSERT INTO `tb_sys_menu` VALUES (71, '商品更新', 68, '/goods/update', 'sys:goods:update', NULL, NULL, NULL, NULL);-- 用户-角色-中间表
DROP TABLE IF EXISTS `tb_sys_user_role`;
CREATE TABLE `tb_sys_user_role`  (`id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',`user_id` bigint NULL DEFAULT NULL COMMENT '用户ID',`role_id` bigint NULL DEFAULT NULL COMMENT '角色ID',`create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '用户角色' ROW_FORMAT = DYNAMIC;INSERT INTO `tb_sys_user_role` VALUES (5, 4, 10, '2025-05-18 11:16:27');
INSERT INTO `tb_sys_user_role` VALUES (6, 4, 11, '2025-05-18 11:16:35');
INSERT INTO `tb_sys_user_role` VALUES (7, 5, 15, '2025-05-18 11:16:37');
INSERT INTO `tb_sys_user_role` VALUES (8, 5, 13, '2024-05-18 11:16:39');
INSERT INTO `tb_sys_user_role` VALUES (9, 5, 14, '2024-05-18 11:16:41');-- 角色-权限【菜单】中间表
DROP TABLE IF EXISTS `tb_sys_role_menu`;
CREATE TABLE `tb_sys_role_menu`  (`id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',`role_id` bigint NULL DEFAULT NULL COMMENT '角色ID',`menu_id` bigint NULL DEFAULT NULL COMMENT '菜单ID',`create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 632 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '角色菜单' ROW_FORMAT = DYNAMIC;INSERT INTO `tb_sys_role_menu` VALUES (632, 10, 64, '2023-05-18 11:17:50');
INSERT INTO `tb_sys_role_menu` VALUES (633, 10, 65, '2023-10-18 11:18:02');
INSERT INTO `tb_sys_role_menu` VALUES (634, 10, 66, '2024-09-18 11:18:18');
INSERT INTO `tb_sys_role_menu` VALUES (635, 10, 67, '2029-05-18 11:18:27');
INSERT INTO `tb_sys_role_menu` VALUES (636, 10, 68, '2024-05-23 11:18:37');
INSERT INTO `tb_sys_role_menu` VALUES (637, 10, 69, '2025-06-18 11:18:49');
INSERT INTO `tb_sys_role_menu` VALUES (638, 10, 70, '2033-09-18 11:19:01');
INSERT INTO `tb_sys_role_menu` VALUES (639, 11, 64, '2027-05-18 11:19:23');
INSERT INTO `tb_sys_role_menu` VALUES (640, 11, 65, '2036-06-18 11:19:39');
INSERT INTO `tb_sys_role_menu` VALUES (641, 15, 68, '2024-12-18 11:20:00');
INSERT INTO `tb_sys_role_menu` VALUES (642, 15, 71, '2022-05-18 11:20:14');
INSERT INTO `tb_sys_role_menu` VALUES (643, 14, 64, '2024-06-21 11:20:50');
INSERT INTO `tb_sys_role_menu` VALUES (644, 14, 65, '2028-05-18 11:21:10');

RBAC跟语言和框架没有啥关系,纯纯一种技术解决方案而已, 不同的安全框架都有不同的实现. 上述五张表,结合自己的实际需求,自行修改,这里我们只是为了做测试而已.

二、spring security 授权处理

2.1 授权的通俗解释

  • 张三是一个普通的个人, 这相当于普通用户
  • 村长、县长、市长这些相当于角色
  • 简单理解, 村长只能管理一个村子, 县长能管理一个县, 市长管理一个市…, 这是权限

张三可以成为一个【村长】, 可以【管理一个村子】, 从大的粒度考量, 因为张三是村长,所以能管理一个村子, 并不是它叫张三, 所以这里可以抽象出来【基于角色信息的权限控制】.

现在张三是一个村长了, 村长可以管理村里的纠纷, 管理村里的财政, 不管是管理纠纷还是村里的财政这些都是村长这个角色对应的一条一条的权限.

以上就是用户、角色、权限的通俗解释,当然它们用户-角色-权限【菜单】之间的关系都是多对多的.

2.2 授权处理

在接口UserDetails 当中

public interface UserDetails extends Serializable {// 权限集合Collection<? extends GrantedAuthority> getAuthorities();
}
public interface GrantedAuthority extends Serializable {String getAuthority();
}

通过以上的接口定义,可以看出来,所谓的权限就是一个字符串而已. 我们常用的一个实现类就是: SimpleGrantedAuthority, 可以查看一下它的类定义

public final class SimpleGrantedAuthority implements GrantedAuthority {private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;private final String role;public SimpleGrantedAuthority(String role) {Assert.hasText(role, "A granted authority textual representation is required");this.role = role;}@Overridepublic String getAuthority() {return this.role;}// 略...
}

通过调用SimpleGrantedAuthority构建方法,可以将一个普通的字符串转换为SimpleGrantedAuthority对象被spring security所识别.
另外一个就是SimpleGrantedAuthority是没有无参构造的,这一点再执行它的对象的序列化、反序列化打时候要特别注意.

2.3 权限字符串

基于角色的授权规则包括 ROLE_ 作为前缀, 也可以通过如下的方式进行修改.

public final class GrantedAuthorityDefaults {private final String rolePrefix;public GrantedAuthorityDefaults(String rolePrefix) {this.rolePrefix = rolePrefix;}/*** The default prefix used with role based authorization. Default is "ROLE_".* @return the default role prefix*/public String getRolePrefix() {return this.rolePrefix;}
}
@Bean
static GrantedAuthorityDefaults grantedAuthorityDefaults() {return new GrantedAuthorityDefaults("RJ_");
}

使用静态方法公开GrantedAuthorityDefaults,以确保 Spring 在初始化 Spring Security 的方法安全性@Configuration类之前发布它

权限字符串分隔符一般以冒号者分隔, 譬如说: sys:user:create, 表示系统模块下的用户服务的创建权限.当然这并不是必须的,一切结合公司规则为准则.

2.4 添加权限字符串

在UserDetailsServiceImpl中,也就是UserDetailsService的实现类,此处我们之前传递的是个空集合,现在传递一些权限字符串.
修改代码如下所示:
2
修改TokenAuthenticationFilter即token的校验过滤器
3

2.5 开启授权处理

前置条件: 授权的前端是当前用户必须经过认证

spring security当中,会使用默认的FilterSecrityInterceptor来进行权限的校验。

  • 从FitlerSecurityInterceptor当中会从SecurityContextHolder获取其中的Authentication, 然后获取权限信息,当前用户是否人的了该资源的访问的权限.
  • 我们需要把当前用户的具体权限也存储到Authentication当点,然后设置我们的资源所需要的权限即可.
  • 使用时候,配置类配置开启注解:@EnableMethodSecurity
  • @EnableMethodSecurity, 开启基于方法的授权
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MethodSecuritySelector.class)
public @interface EnableMethodSecurity {
}

这里有两个特别重要的属性:

  • boolean prePostEnabled() default true;表示注解@PreAuthorize、 @PostAuthorize、 @PreFilter和 @PostFilter是否开启,默认就是【开启】状态
  • boolean securedEnabled() default false; 表示注解@secured是否开启, true,表示开启, false,关闭

2.6 编写测试接口

在HomeController当中, 添加如下测试代码

 // 测试权限字符串// 表示只有当前用户有: ROLE_user这个角色才测出访问这个接口@Secured("ROLE_user")@GetMapping("/api/pub/v1/list")public Result menu(){List<String> menuList = new ArrayList<>();menuList.add("商品管理");menuList.add("用户管理");menuList.add("查看商品");menuList.add("查看客户信息");return Result.success(0, "获取列表", menuList);}

@Secured(“ROLE_user”), 于角色的权限控制注解。可以指定一个或多个角色,只有具有这些角色的用户才能执行被注解的方法。可以指定一个或者多个角色的字符串,写法简单,但是功能也十分简单,以上表示,当前认证的用户如果拥有ROLE_user这个角色, 那么接口可以访问,如果当前认证的用户没有ROLE_user角色,那么无法访问接口,会抛出异常.

2.7 前端测试工程添加代码并验证

// 测试接口,主要验证权限字符串
export const getMneuListTest = () => $http({url: '/list', method: 'get'})
const m1 = async () => {const {code, msg, data} = await getMneuListTest()if(code === 0){content.value = data}else {ElMessage.error({type: 'error',message: msg || '操作异常',showClose: true})}
}

3
启动服务器测试
5
在这里插入图片描述
再添加一个测试接口

// 测试只有admin能看的数据
@Secured("ROLE_admin")
@GetMapping("/api/pub/v1/admin")
public Result admin(){return Result.success(0, "操作成功", "数据只有admin角色能看");
}

前端写接口,访问即可,可以看到,由于当前认证的用户并没有User_admin这个角色,返回访问接口会返回403.
6

2.8 授权异常自定义处理

如果用户没有相关访问去访问资源,默认会抛出AccessDeniedException异常, 当该异常发生的时候,我们可以通过AccessDeniedHandler接口,进行自定义处理.在spring security配置文件当中进行配置, 如下所示:

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {return httpSecurity.authorizeHttpRequests(authorize -> {try {authorize.requestMatchers("/api/pub/v1/login").permitAll().requestMatchers("/static/**", "/resources/**").permitAll().anyRequest().authenticated();} catch (Exception e) {throw new RuntimeException(e);}}).csrf(AbstractHttpConfigurer::disable)// 将我们自己定义的过滤器添加到 UsernamePasswordAuthenticationFilter过滤器之前.addFilterBefore(tokenAuthenticationFilter, UsernamePasswordAuthenticationFilter.class).cors(c -> {// 处理spring security跨域配置c.configurationSource(apiConfigurationSource());}).exceptionHandling(exception -> exception.accessDeniedHandler(new AccessDeniedHandler() { // 处理未授权的情况, 访问资源的情况.@Overridepublic void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException, IOException {response.setContentType("application/json;charset=utf-8");Result result = Result.error(403, "您没有权限操作", "权限不足,请联系管理员!");PrintWriter writer = response.getWriter();writer.write(JSON.toJSONString(result));}})).build();
}

再次测试
5
异常信息
90

2.9 验证权限字符串

编写测试接口

// 只有拥有sys:user:list权限才能访问
@PreAuthorize(value = "hasAuthority('sys:user:list')")
@GetMapping("/api/pub/v1/user")
public Result userList(){return Result.success(0, "操作成功", "数据只有sys:user:list角色能看");
}// 只有拥有sys:user:create权限才能访问
@PreAuthorize(value = "hasAuthority('sys:user:create')")
@GetMapping("/api/pub/v1/create")
public Result userCreate(){return Result.success(0, "操作成功", "数据只有sys:user:create角色能看");
}

@PreAuthorize(value = “hasAuthority(‘sys:user:create’)”), 该注解是我们最最常用的注解,没有之一.

  • @PreAuthorize是 Spring Security 中用于方法级别的访问控制的注解。
    • 权限检查:
      • 在方法执行之前,@PreAuthorize会根据指定的表达式来判断当前用户是否具有执行该方法的权限。如果表达式的结果为false,则会抛出一个访问拒绝异常,阻止方法的执行。
      • 这个注解可以用于控制对特定业务方法的访问,确保只有具有相应权限的用户才能执行这些方法。
    • 表达式语言支持
      • @PreAuthorize支持使用 SpEL(Spring Expression Language)表达式来定义权限检查逻辑。SpEL 提供了丰富的语法和功能,可以进行各种复杂的权限判断。
      • 例如,可以使用表达式检查用户的角色、权限、属性等信息,以确定是否允许执行方法。 ’

基于角色的权限检查使用示例:

  • @PreAuthorize(“hasRole(‘ADMIN’)”):表示只有具有 “ADMIN” 角色的用户才能执行该方法。
  • @PreAuthorize(“hasAnyRole(‘ADMIN’, ‘MANAGER’)”):表示具有 “ADMIN” 或 “MANAGER” 角色的用户可以执行该方法。

基于权限的权限检查

  • @PreAuthorize(“hasPermission(#order, ‘WRITE’)”):假设存在一个自定义的权限检查方法hasPermission,这个注解表示只有当当前用户对给定的订单对象order具有 “WRITE” 权限时,才能执行该方法

基于用户属性的权限检查

  • @PreAuthorize(“authentication.principal.username.equals(‘admin’)”):表示只有用户名是 “admin” 的用户才能执行该方法。

56
123

2.10 从表中查询出权限

根据用户id,查询出对应的角色信息

@Mapper
public interface TbSysUserMapper extends BaseMapper<TbSysUser> {@Select("select * from tb_sys_role where id in (select role_id from tb_sys_user_role where user_id = #{id})")List<TbSysRole> findSysRoleByUserId(Long id);@Select("select * from tb_sys_menu where id in (select menu_id from tb_sys_role_menu where role_id in (select role_id from tb_sys_user_role where user_id = #{id}))")List<TbSysMenu> findSysMenuByUserId(Long id);
}

单元测试一下
zhangsan账号id = 4, 角色信息如下所示
123
zhangsan账号id = 4的权限列表
345
jack的账号id=5, 角色信息如下所示
78
jack的账号id=5, 权限信息
222
修改UserDetailsServiceImp获取权限列表
1234
TokenAuthenticationFilter获取权限列表
21
编写一个测试接口

// 只有拥有sys:goods:delete权限才能访问
@PreAuthorize(value = "hasAuthority('sys:goods:delete')")
@GetMapping("/api/pub/v1/delete")
public Result userDelete(){return Result.success(0, "操作成功", "商品删除成功: 数据只有sys:goods:delete权限能看");
}

登录zhangsan账号
121
99
登录jack账号
543
1234
验证完成

三、总结

  • RBAC模型五张表
  • 权限校验的三个注解
    • @EnableMethodSecurity(securedEnabled = true)
    • @PreAuthorize
    • @Secured
  • MySQL多表查询

注意事项:

  • 在token校验的过滤器当中,频繁的操作数据库,可能会带来性能瓶颈, 我们可以引入Redis来存储认证对象,避免频繁的操作数据库

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

企业微信:客户联系自带群发工具和聊天工具

前言 上篇博客介绍了一些客户联系的开启和配置&#xff0c;接下来我们来使用客户联系自带群发工具和聊天工具。 突然发现官方的文档已经很详细了&#xff0c;我这里给出一些简单的描述&#xff1a; 企业微信如何使用群发助手&#xff1f;-帮助中心-企业微信 群发工具 群发消息给…

Python 中的lambda函数表达式

lambda x:xn 这是一个 Python 中的lambda函数表达式。它定义了一个匿名函数&#xff0c;该函数接受一个参数x&#xff0c;并返回xn的值。再定义常数n。 n 5 my_function lambda x: x n print(my_function(3)) 运行结果&#xff1a; 在上述代码中&#xff0c;首先定义了…

win10专业版永久关闭自动更新功能

如何关闭 Windows 10 自动更新 Windows 10 默认情况下会自动安装更新&#xff0c;这可能会导致系统不稳定或问题。如果您想关闭自动更新&#xff0c;可以使用以下方法&#xff1a; 方法 1&#xff1a;使用设置应用程序 打开“设置”应用程序。转到“更新和安全”。在“Windo…

获取本函数所在代码域内的所有局部变量和值以字典形式返回locals()

【小白从小学Python、C、Java】 【考研初试复试毕业设计】 【Python基础AI数据分析】 获取本函数所在代码域内的 所有局部变量和值 以字典形式返回 locals() 选择题 关于以下代码输出的结果说法正确的是&#xff1f; a 10 def x(): b 20 print(locals()) print("【执行…

TTT大语言模型架构发布,成功撼动了Transformer与Mamba模型

上期图文&#xff0c;我们刚介绍了 transformer 模型与 Mamba 模型&#xff0c;觉得 Mamba 模型的发布可以有效改善 transformer 模型长序列输入复杂度的问题&#xff0c;应该能够成为下一个大语言模型的基石。谁想Mamba2 还没有发布几天&#xff0c;这边最新的大语言模型TTT模…

隆道携手黑龙江省中小企业协会助力区域企业数字化转型

9月28日&#xff0c;隆道公司总裁吴树贵出席“2024年黑龙江省中小企业协会副会长扩大会议暨数字化转型专题报告会”并做主题发言&#xff0c;分享了龙江惠企商城建设情况和供应链数字化转型实践经验。会上&#xff0c;隆道公司与黑龙江省中小企业协会签订了战略合作协议&#x…

【Kubernetes知识点】 解读 Service 和 EndpointSlice 之间的关系

【Kubernetes知识点】 解读 Service 和 EndpointSlice 之间的关系 目录 1 概念 1.1 Service的概念1.2 Endpoint 的概念1.3 EndpointSlice 的引入 1.3.1 EndpointSlice支持的地址1.3.2 EndpointSlice的状态1.3.3 EndpointSlice的拓扑信息 1.4 Service 、Endpoint和 EndpointSl…

自动驾驶TPM技术杂谈 ———— 高精度地图

文章目录 概述高精度地图分层架构价值体现 关键技术道路元素图像处理激光点云处理点云特征提取点云法向量点云配准点云分割 同步定位与地图构建高精度地图云端服务体系 解决方案高精度地图采集数据模型 高精度地图制作和编译数据处理编译及格式规范NDSOpenDRIVE 高精度地图质量…

入职2年的程序员,被劝退了!年纪大了,感觉好绝望!

入职2年的程序员&#xff0c;今天被劝退了&#xff01;年纪大了&#xff0c;感觉好绝望&#xff01; 我的朋友是一位程序员&#xff0c;毕业后去了BAT企业&#xff0c;前2年去了一家国企&#xff0c;至今刚满2年&#xff0c;刚进去绩效领导给打了C&#xff0c;现在被边缘化&…

可视化是工业互联网的核心技术之一,都有哪些应用场景?

一、工业互联网是什么&#xff0c;发展的来胧去脉 工业互联网是指利用互联网技术和物联网技术&#xff0c;将工业生产中的各种设备、机器、传感器等进行互联互通&#xff0c;实现信息的实时采集、传输和分析&#xff0c;从而实现生产过程的智能化、自动化和高效化。 工业互联网…

echarts实现3D柱状图(视觉层面)根据博主改编

https://blog.csdn.net/weixin_57798646/article/details/131067725 这是原贴 在这个基础上我需要实现 一根柱子 代码如下 <!DOCTYPE html> <html lang"en" style"height: 100%"><head><meta charset"utf8"> </hea…

Python画笔案例-069 绘制调皮田彩格

1、绘制调皮田彩格 通过 python 的turtle 库绘制 调皮田彩格,如下图: 2、实现代码 绘制 调皮田彩格,以下为实现代码: """调皮田彩格.py本程序需要coloradd模块支持,安装方法:pip install coloradd =""" import turtle from coloradd import…

AIGC实践|AI助力文旅短视频创作全流程

前言&#xff1a; 受到央视《AI我中华》及各地文旅AI宣传片的启发&#xff0c;本次我将尝试使用AI辅助进行城市宣传片的创作探索。我将尽可能详细的展示使用AI辅助创作城市宣传片的全过程&#xff0c;从灵感捕捉到最终成品呈现。现在&#xff0c;让我们一同踏上这段充满创意的探…

工作日志:el-table在无数据情况下,出现横向滚动条。

1、遇到一个警告。 原因&#xff1a;中的组件不能呈现动画的非元素根节点。 也就是说&#xff0c;Transition包裹的必须是一个单根的组件。 2、el-table在无数据情况下&#xff0c;出现横向滚动条&#xff0c;大概跟边框的设置有关系。 开始排查。 给.el-scrollbar加了一个…

ChatGPT+R语言强强联合,数据分析不再难!回归与混合效应模型、多元统计分析、结构方程模型(SEM)(lavaan)、Meta分析、贝叶斯回归等应用

目录 第一章 生态环境数据统计概述及基础 第二章 GPT&R&#xff1a;回归与混合效应模型 第三章 GPT&R&#xff1a;多元统计分析 第四章 GPT&R&#xff1a;结构方程模型&#xff08;SEM&#xff09;&#xff08;lavaan&#xff09; 第五章 GPT&R&#xff1…

C++ STL容器(四) —— vector底层剖析

这篇讲解vector&#xff0c;不说废话&#xff0c;直接开始&#xff01; 文章目录 原理UML类图代码实现构造函数插入元素删除元素清空容器析构函数赋值运算符 案例分析 原理 这里简单说一下 vector 的大致思想&#xff0c;动态数组&#xff0c;即它的长度会随着我们插入元素而产…

安全帽识别摄像机

安全帽识别摄像机 是一种结合了监控摄像技术和智能分析技术的先进设备&#xff0c;旨在通过实时监测和分析人员头部是否佩戴安全帽&#xff0c;识别出未佩戴安全帽的情况&#xff0c;并及时发出警报通知相关人员。这种摄像机在建筑工地、工厂车间、交通运输等领域有着广泛的应用…

本省第一所!新大学,揭牌!

9月26日&#xff0c;海南艺术职业学院举行揭牌仪式&#xff0c;标志着海南省第一所公办艺术类高等职业院校正式揭牌成立。海南省旅文厅党组成员、副厅长刘成出席揭牌仪式&#xff0c;省教育厅党组成员、副厅长邢孔政在揭牌仪式上宣读省人民政府同意设立海南艺术职业学院的批复。…

jmeter进行性能测试实践

设置场景接口 一、通过抓取一个场景的接口&#xff08;抓包&#xff09; 自己抓取需要的接口&#xff0c;进行依赖 流程&#xff1a;1.在网页上F12抓取登录页面和登出页面的URL。2.在jemeter设置线程组&#xff0c;添加http请求输入URL等。3.查看结果数 二、通过boday录制 …

Linux之实战命令20:split应用实例(五十四)

简介&#xff1a; CSDN博客专家、《Android系统多媒体进阶实战》一书作者 新书发布&#xff1a;《Android系统多媒体进阶实战》&#x1f680; 优质专栏&#xff1a; Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a; 多媒体系统工程师系列【…