一文上手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.mzph.cn/bicheng/55439.shtml

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

相关文章

二分查找算法专题(1)

找往期文章包括但不限于本期文章中不懂的知识点&#xff1a; 个人主页&#xff1a;我要学编程(ಥ_ಥ)-CSDN博客 所属专栏&#xff1a; 优选算法专题 目录 二分查找算法的介绍 704. 二分查找 34. 在排序数组中查找元素的第一个和 最后一个位置 35. 搜索插入位置 69. x的平…

【光伏混合储能】VSG并网运行,构网型变流器,虚拟同步机仿真

摘要 本文提出了一种基于光伏发电与混合储能系统结合的虚拟同步发电机&#xff08;VSG&#xff09;控制策略&#xff0c;该策略能够在并网运行时稳定电网电压和频率。通过仿真分析&#xff0c;验证了该策略在各种运行工况下的有效性&#xff0c;展示了其在电力系统中的广泛应用…

CORE MVC 过滤器 (筛选器)《2》 TypeFilter、ServiceFilter

TypeFilter、ServiceFilter ServiceFilter vs TypeFilter ServiceFilter和TypeFilter都实现了IFilterFactory ServiceFilter需要对自定义的Filter进行注册&#xff0c;TypeFilter不需要 ServiceFilter的Filter生命周期源自于您如何注册&#xff08;全局、区域&#xff09;&…

SpringCloud-基于Docker和Docker-Compose的项目部署

一、初始化环境 1. 卸载旧版本 首先&#xff0c;卸载可能已存在的旧版本 Docker。如果您不确定是否安装过&#xff0c;可以直接执行以下命令&#xff1a; sudo yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logro…

了解芯片光刻与OPC

欢迎关注更多精彩 关注我&#xff0c;学习常用算法与数据结构&#xff0c;一题多解&#xff0c;降维打击。 参考资料&#xff1a; 光刻技术与基本流程 https://www.bilibili.com/video/BV1tP4y1j7BA OPC https://www.bilibili.com/video/BV1o94y1U7Td 论文&#xff1a;计算…

[网络]抓包工具介绍 tcpdump

一、tcpdump tcpdump是一款基于命令行的网络抓包工具&#xff0c;可以捕获并分析传输到和从网络接口流入和流出的数据包。 1.1 安装 tcpdump 通常已经预装在大多数 Linux 发行版中。如果没有安装&#xff0c;可以使用包管理器 进行安装。例如 Ubuntu&#xff0c;可以使用以下…

DBeaver显示PostgreSQL数据库的信息模式

DBeaver连接PostgreSQL数据库后&#xff0c;默认情况下是不加载信息模式的&#xff0c;如果有需要&#xff0c;我们可以通过设置显示信息模式。 具体步骤&#xff1a;点击数据库连接–>右键打开设置–>连接设置–>常规–>导航视图–>自定义–>勾选显示系统对…

宁夏众智科技OA办公系统存在SQL注入漏洞

漏洞描述 宁夏众智科技OA办公系统存在SQL注入漏洞 漏洞复现 POC POST /Account/Login?ACTIndex&CLRHome HTTP/1.1 Host: Content-Length: 45 Cache-Control: max-age0 Origin: http://39.105.48.206 Content-Type: application/x-www-form-urlencoded Upgrade-Insecur…

【在Linux世界中追寻伟大的One Piece】System V共享内存

目录 1 -> System V共享内存 1.1 -> 共享内存数据结构 1.2 -> 共享内存函数 1.2.1 -> shmget函数 1.2.2 -> shmot函数 1.2.3 -> shmdt函数 1.2.4 -> shmctl函数 1.3 -> 实例代码 2 -> System V消息队列 3 -> System V信号量 1 -> Sy…

【MySQL 06】表的增删查改

目录 1.insert 增添数据 1.1单行数据 全列插入 1.2多行数据 指定列插入 1.3插入否则更新 1.4.插入否则替换 2.select查找 2.1 全列查找 2.2指定列查找 2.3查询字段为表达式 2.4为查询结果指定别名 2.5 结果去重 2.6 where条件查询 2.7结果排序 2.8.筛选分页结果…

侧边菜单的展开和折叠

环境准备&#xff1a;Vue3Element-UI Plus <script setup> import {ref} from "vue";// 是否折叠菜单&#xff0c;默认折叠 const isCollapse ref(true)// 退出登录 function logout() {alert(退出) }// 个人中心 function profile() {alert(个人中心) } <…

设计模式、系统设计 record part02

软件设计模式&#xff1a; 1.应对重复发生的问题 2.解决方案 3.可以反复使用 1.本质是面向对象 2.优点很多 1.创建型-创建和使用分离 2.结构型-组合 3.行为型-协作 571123种模式 UML-统一建模语言-Unified Modeling Language 1.可视化&#xff0c;图形化 2.各种图&#xff08;9…

[Unity Demo]从零开始制作空洞骑士Hollow Knight第十一集:制作法术系统的回血机制和火球机制

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、制作法术系统的回血机制 1.制作动画以及使用UNITY编辑器编辑2.使用代码和PlaymakerFSM制作回血机制二、制作法术系统的火球机制 1.制作动画以及使用UNITY编…

鸿蒙开发知识点速记全解

入门 1、API涵盖应用框架、系统、媒体、图形、应用服务、AI六大领域。 应用框架相关Kit开放能力&#xff1a;Ability Kit&#xff08;程序框架服务&#xff09;、ArkUI&#xff08;方舟UI框架&#xff09;等。系统相关Kit开放能力&#xff1a;Universal Keystore Kit&#xf…

测试管理新增视图与高级搜索功能,测试计划支持一键生成缺陷详情,MeterSphere开源持续测试工具v3.3版本发布

2024年9月29日&#xff0c;MeterSphere开源持续测试工具正式发布v3.3版本。 在这一版本中&#xff0c;接口测试方面&#xff0c;接口导入功能支持导入Postman、JMX、HAR和MeterSphere格式的文件&#xff0c;接口场景的自定义请求步骤支持cURL快捷导入&#xff1b;测试管理方面…

HarmonyOS/OpenHarmony 如何将rawfile中文件复制到沙箱中

关键词&#xff1a;h5离线加载、HarmonyOS、OpenHarmony、文件操作、复制、解压 当下有一个场景&#xff0c;需要离线加载 h5离线资源zip包&#xff0c;并实现资源包的动态更新&#xff0c;那么仅靠 $rawfile并不能实现该功能&#xff0c;那么我们该如何实现&#xff1f; 我们…

在线代码编辑器

在线代码编辑器 文章说明前台核心代码后台核心代码效果展示源码下载 文章说明 采用Java结合vue3设计实现的在线代码编辑功能&#xff0c;支持在线编辑代码、运行代码&#xff0c;同时支持导入文件&#xff0c;支持图片识别&#xff0c;支持复制代码&#xff0c;可将代码导出为图…

【mod分享】侠盗猎魔2冬日mod,贴图高清化,增加下雪场景,支持光追,并且增加红色霓虹灯

今天小编为大家带来一个新的游戏mod&#xff0c;这次mod主要是修改了游戏《侠盗猎魔2》&#xff0c;我给游戏增加了下雪的场景&#xff0c;并且增加了红色的霓虹灯&#xff0c;整体让游戏沉浸在一种诡异的圣诞气氛中。并且我还提高了游戏材质的分辨率。更多细节需要玩家自己探索…

【STM32开发笔记】移植AI框架TensorFlow到STM32单片机【下篇】

【STM32开发笔记】移植AI框架TensorFlow到STM32单片机【下篇】 一、上篇回顾二、项目准备2.1 准备模板项目2.2 支持计时功能2.3 配置UART4引脚2.4 支持printf重定向到UART42.5 支持printf输出浮点数2.6 支持printf不带\r的换行2.7 支持ccache编译缓存 三、TFLM集成3.1 添加tfli…

设计模式-策略模式-200

优点&#xff1a;用来消除 if-else、switch 等多重判断的代码&#xff0c;消除 if-else、switch 多重判断 可以有效应对代码的复杂性。 缺点&#xff1a;会增加类的数量&#xff0c;有的时候没必要为了消除几个if-else而增加很多类&#xff0c;尤其是那些类型又长又臭的 原始代…