当前位置: 首页 > news >正文

Spring Boot 3 集成 Spring Security(3)数据管理

文章目录

    • 准备工作
      • 新建项目
      • 引入MyBatis-Plus依赖
      • 创建表结构
      • 生成基础代码
    • 逻辑实现
      • `application.yml`配置
      • SecurityConfig 配置
      • 自定义 UserDetailsService
      • 创建测试
    • 启动测试

在前面的文章中我们介绍了 《Spring Boot 3 集成 Spring Security(1)认证》和 《Spring Boot 3 集成 Spring Security(2)授权》,这篇博客将介绍如何在 Spring Boot 3 项目中,整合 Spring Security 和 MyBatis-Plus ,轻松实现基于数据库的用户访问控制、权限管理。

准备工作

新建项目

springboot3-security-mysql-example 引入依赖

在这里插入图片描述

引入MyBatis-Plus依赖

MyBatis-Plus 是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

注意事项

版本 3.5.9+ 开始修改为可选依赖,具体查看下文 maven bom 部分。

  <mybatisplus.version>3.5.9</mybatisplus.version>
    <!-- MyBatis-Plus https://baomidou.com--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-spring-boot3-starter</artifactId></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-jsqlparser</artifactId></dependency>
    <dependencyManagement><dependencies><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-bom</artifactId><version>${mybatisplus.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement>

创建表结构

这里我们定义三张表,来实现用户角色权限的操作

在这里插入图片描述

-- 用户表
CREATE TABLE `sys_user` (`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT 'ID',`username` VARCHAR ( 64 ) DEFAULT NULL COMMENT '用户名',`password` VARCHAR ( 64 ) DEFAULT NULL COMMENT '密码',`sex` CHAR ( 1 ) DEFAULT '0' COMMENT '性别 0 男 1 女 2 未知',`nick_name` VARCHAR ( 64 ) DEFAULT NULL COMMENT '昵称',`status` CHAR ( 1 ) DEFAULT '1' COMMENT '账号状态 0 禁用 1 启用',`valid` INT DEFAULT '1' COMMENT '有效状态 0 无效 1 有效',
PRIMARY KEY ( `id` ) 
) ENGINE = INNODB COMMENT = '用户';INSERT INTO `sys_user` (`id`, `username`, `password`, `sex`, `nick_name`, `status`, `valid`) VALUES (1, 'admin', '$2a$10$xZdonloiiL6YfoLZv6mrJuvxtD238uHPIKkVDpQKBuZxzMDpTf8uK', '0', '管理员张三', '1', 1);
INSERT INTO `sys_user` (`id`, `username`, `password`, `sex`, `nick_name`, `status`, `valid`) VALUES (2, 'user', '$2a$10$evM9SfvuN/E.ykWWOf6b3eTltPvuc6XjwW/qIhagSjlsTfi9l26Ba', '0', '用户李四', '1', 1);-- 角色表
CREATE TABLE `sys_role` (`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT 'ID',`name` VARCHAR ( 64 ) DEFAULT NULL COMMENT '角色名',`code` VARCHAR ( 64 ) DEFAULT NULL COMMENT '密码',`status` CHAR ( 1 ) DEFAULT '1' COMMENT '状态 0 禁用 1 启用',`valid` INT DEFAULT '1' COMMENT '有效状态 0 无效 1 有效',
PRIMARY KEY ( `id` ) 
) ENGINE = INNODB COMMENT = '角色';INSERT INTO `sys_role` (`id`, `name`, `code`, `status`, `valid`) VALUES (1, '管理员', 'ROOT', '1', 1);
INSERT INTO `sys_role` (`id`, `name`, `code`, `status`, `valid`) VALUES (2, '普通用户', 'USER', '1', 1);-- 用户角色关系表
CREATE TABLE `sys_user_role` (`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT 'ID',`user_id` bigint DEFAULT NULL COMMENT '用户ID',`role_id` bigint DEFAULT NULL COMMENT '角色ID',PRIMARY KEY (`id`) USING BTREE
) ENGINE = INNODB COMMENT = '用户角色关系表';INSERT INTO `sys_user_role` (`id`, `user_id`, `role_id`) VALUES (1, 1, 1);
INSERT INTO `sys_user_role` (`id`, `user_id`, `role_id`) VALUES (2, 2, 2);

默认设置账户密码123456,在数据库中使用加密后的密码,关于密码加密,可以使用下面的测试方法。

    public static void main(String[] args) {BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();String result = encoder.encode("123456");// 输出加密后的密码System.out.println(result);// 对比加密后的密码和明文密码System.out.println(encoder.matches("123456", result));}

生成基础代码

这里我用了代码生成器插件,以提高生产效率,想具体了解,可以去官网上搭建使用。这里就不多说啦

在这里插入图片描述

本地代码勾选,使用 mybatis-plus 3

在这里插入图片描述

在这里插入图片描述

准备工作到这里基本上就可以了,接下来开始实现从数据库中读取用户角色权限

逻辑实现

application.yml配置

spring:thymeleaf:# 设置Thymeleaf模板文件的前缀位置(默认是`src/main/resources/templates`)prefix: classpath:/templates/# 设置模板文件的后缀(默认是`.html`)suffix: .html# 设置模板模式(默认是HTML5,Thymeleaf 3中为`HTML`)mode: HTML# 开启模板缓存(开发时建议关闭,生产时开启)cache: falsedatasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://127.0.0.1:3306/security_data?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghaiusername: rootpassword: root#mybatis
mybatis-plus:mapper-locations: classpath*:/mapper/**/*.xml#实体扫描,多个package用逗号或者分号分隔typeAliasesPackage: cn.harry.*.domainglobal-config:#数据库相关配置db-config:#主键类型  AUTO:"数据库ID自增", INPUT:"用户输入ID", ID_WORKER:"全局唯一ID (数字类型唯一ID)", UUID:"全局唯一ID UUID";id-type: AUTO# 逻辑删除全局属性名(驼峰和下划线都支持)logic-delete-field: validlogic-delete-value: 0 # 逻辑已删除值(默认为 1)logic-not-delete-value: 1 # 逻辑未删除值(默认为 0)banner: false#原生配置configuration:map-underscore-to-camel-case: truecache-enabled: falsecall-setters-on-nulls: truejdbc-type-for-null: 'null'# 这个配置会将执行的sql打印出来,在开发或测试的时候可以用log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

SecurityConfig 配置

要使用 Spring Security 进行用户认证,我们需要配置 SecurityConfig,并实现自定义的 UserDetailsService 来与数据库中的用户信息进行集成。


/*** @author harry* @公众号 Harry技术*/
@Configuration
@EnableWebSecurity
@EnableMethodSecurity(securedEnabled = true) // 开启方法级别的权限控制
@RequiredArgsConstructor
public class SecurityConfig {// 通过构造函数注入自定义UserDetailsServiceprivate final UserDetailsService userDetailsService;@Beanpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests(auth -> auth// 公开访问.requestMatchers("/").permitAll()// 其他接口需认证.anyRequest().authenticated()).userDetailsService(userDetailsService)
//                .exceptionHandling(exception -> exception
//                        .authenticationEntryPoint(restAuthenticationEntryPoint)
//                        .accessDeniedHandler(restfulAccessDeniedHandler)
//                )// 开启基于表单的登录.formLogin(Customizer.withDefaults())//                // 开启注销功能
//                .logout(Customizer.withDefaults())
//                // 开启 HTTP Basic 认证
//                .httpBasic(Customizer.withDefaults())
//                // 开启 CSRF 防护
//                .csrf(Customizer.withDefaults())
//                // 开启跨域资源共享
//                .cors(Customizer.withDefaults());return http.build();}@Beanpublic PasswordEncoder passwordEncoder() {// 使用 BCrypt 进行密码加密return new BCryptPasswordEncoder();}

自定义 UserDetailsService

想从数据库加载用户信息,就需要创建一个自定义的 UserDetailsService 实现类,它的主要作用:

用户认证:
UserDetailsService 负责从数据源(如数据库、LDAP等)中加载用户特定的安全信息,包括用户名、密码和权限(角色)。
Spring Security 使用 UserDetailsService 来验证用户提供的凭据是否正确。
用户授权:
加载用户的权限信息,以便在认证成功后进行授权检查。
权限信息通常包括用户的角色(如 ROLE_ADMIN, ROLE_USER 等),这些角色用于控制用户可以访问的资源和操作。

/*** 系统用户认证  service** @author harry* @公众号 Harry技术*/
@Slf4j
@Service
@RequiredArgsConstructor
public class UserDetailsServiceImpl implements UserDetailsService {private final SysUserMapper sysUserMapper;private final SysUserRoleMapper sysUserRoleMapper;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {try {// 获取登录用户信息SysUser user = sysUserMapper.selectByUsername(username);// 用户不存在、用户停用 等校验 TODOLong useId = user.getId();// 获取角色Set<String> roles = sysUserRoleMapper.listRoleKeyByUserId(useId);return new SysUserDetails(user, roles, username);} catch (Exception e) {log.error("loadUserByUsername error", e);}return null;}
}

我们根据数据库中的用户信息加载用户,并将角色转换为 Spring Security 能识别的格式。我们写一个SysUserDetails类来实现自定义Spring Security 用户对象。


/*** 自定义 Spring Security 用户对象** @author harry* @公众号 Harry技术*/
@Data
@NoArgsConstructor
public class SysUserDetails implements UserDetails {private String username;private SysUser sysUser;private Collection<SimpleGrantedAuthority> authorities;public SysUserDetails(SysUser user, Set<String> roles, String username) {this.sysUser = user;Set<SimpleGrantedAuthority> authorities;if (CollectionUtil.isNotEmpty(roles)) {// 标识角色 前面加上 ROLE_authorities = roles.stream().map(role -> new SimpleGrantedAuthority("ROLE_" + role)).collect(Collectors.toSet());} else {authorities = Collections.emptySet();}this.authorities = authorities;this.username = username;}@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {// 返回当前用户的权限return authorities;}@Overridepublic String getPassword() {return sysUser.getPassword();}@Overridepublic String getUsername() {return this.username;}/*** 是否可用 ,禁用的用户不能身份验证** @return 是否可用*/@Overridepublic boolean isEnabled() {return StatusEnums.ENABLE.getKey().equals(sysUser.getStatus());}
}

创建测试

  • 页面
  <!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Title</title><style>.content {width: 800px;height: 800px;text-align: center;line-height: 100px;font-size: 20px;flex: 1;flex-direction: column;display: flex;justify-content: center;align-items: center;}</style></head><body><div class="content"><!--去登陆 --><a href="/login">去登陆</a><!-- admin/info 接口 --><a href="/admin/info">访问 admin/info 接口</a><!-- 去首页 --><a href="/user/info">访问 user/info 接口</a><!--退出--><a href="/logout">退出</a></div></body></html>
  • 接口

    改写接口admin/info,并配置 @PreAuthorize("hasRole('ROOT')")只有 ADMIN 角色才能访问

  /*** @author harry* @公众号 Harry技术* Spring Boot 3 集成 Spring Security(2) 授权: https://mp.weixin.qq.com/s/HzzcYIQLnch_7r7wdUarew*/@Slf4j@RestControllerpublic class AdminController {@GetMapping("/admin/info")@PreAuthorize("hasRole('ROOT')")  // 只有 ADMIN 角色才能访问public SysUserDetails adminInfo() {// 获取当前登录的用户信息SysUserDetails user = (SysUserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();log.info("当前登录的用户信息:{}", user.toString());return user;}}

改写接口user/info,并配置 @PreAuthorize("hasRole('USER')")只有 USER 角色才能访问

      @Slf4j@RestControllerpublic class UserController {@GetMapping("/user/info")@PreAuthorize("hasRole('USER')")  // 只有 user 角色才能访问public SysUserDetails userInfo() {// 获取当前登录的用户信息SysUserDetails user = (SysUserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();log.info("当前登录的用户信息:{}", user.toString());return user;}}

启动测试

  • 1.登录admin账户

访问 admin/info 接口

在这里插入图片描述

访问 user/info 接口

在这里插入图片描述

  • 2.登录user用户

访问 admin/info 接口

在这里插入图片描述

访问 user/info 接口

在这里插入图片描述

通过上面的测试用例,通过定义用户和角色实体、实现自定义的 UserDetailsService,实现了数据库驱动的用户认证和基于角色的授权机制。这种结合方式不仅在安全性上提供了极大的灵活性,也让数据管理变得更加简洁高效。

关注我,在后续的文章中,我们进一步探讨如果使用JWT、OAuth2 等机制、使用Redis作为缓存来强化认证与授权的实现。

示例源码:关注公众号“Harry技术”,回复 security 获取源码地址。

相关文章:

Spring Boot 3 集成 Spring Security(3)数据管理

文章目录 准备工作新建项目引入MyBatis-Plus依赖创建表结构生成基础代码 逻辑实现application.yml配置SecurityConfig 配置自定义 UserDetailsService创建测试 启动测试 在前面的文章中我们介绍了 《Spring Boot 3 集成 Spring Security&#xff08;1&#xff09;认证》和 《…...

书生大模型实战营第四期-入门岛-4. maas课程任务

书生大模型实战营第四期-入门岛-4. maas课程任务 任务一、模型下载 任务内容 使用Hugging Face平台、魔搭社区平台&#xff08;可选&#xff09;和魔乐社区平台&#xff08;可选&#xff09;下载文档中提到的模型&#xff08;至少需要下载config.json文件、model.safetensor…...

Spring ApplicationListener监听

【JavaWeb】Spring ApplicationListener-CSDN博客 ApplicationEvent以及Listener是Spring为我们提供的一个事件监听、订阅的实现&#xff0c;内部实现原理是观察者设计模式&#xff0c;设计初衷也是为了系统业务逻辑之间的解耦&#xff0c;提高可扩展性以及可维护性。事件发布…...

K8s调度器扩展(scheduler)

1.K8S调度器 筛选插件扩展 为了熟悉 K8S调度器扩展步骤&#xff0c;目前只修改 筛选 插件 准备环境&#xff08;到GitHub直接下载压缩包&#xff0c;然后解压&#xff0c;解压要在Linux系统下完成&#xff09; 2. 编写调度器插件代码 在 Kubernetes 源代码目录下编写调度插件…...

IntelliJ IDEA 中,自动导包功能

在 IntelliJ IDEA 中&#xff0c;自动导包功能可以极大地提高开发效率&#xff0c;减少手动导入包所带来的繁琐和错误。以下是如何在 IntelliJ IDEA 中设置和使用自动导包功能的详细步骤&#xff1a; 一、设置自动导包 打开 IntelliJ IDEA&#xff1a; 启动 IntelliJ IDEA 并打…...

Spring事务笔记

目录 1.Spring 编程式事务 2.Transactional 3.事务隔离级别 4.Spring 事务传播机制 什么是事务? 事务是⼀组操作的集合, 是⼀个不可分割的操作. 事务会把所有的操作作为⼀个整体, ⼀起向数据库提交或者是撤销操作请求. 所以这组操作要么同时成 功, 要么同时失败 1.Spri…...

SQLite 管理工具 SQLiteStudio 3.4.5 发布

SQLiteStudio 3.4.5 版本现已发布&#xff0c;它带来了大量的 bug 修复&#xff0c;并增加了一些小功能。SQLiteStudio 是一个跨平台的 SQLite 数据库的管理工具。 具体更新内容包括&#xff1a; 现在可以使用 Collations Editor 窗口在数据库中注册 Extension-based collatio…...

QT 实现组织树状图

1.实现效果 在Qt中使用QGraphicsItem和QGraphicsScene实现树状图,你需要创建自定义的QGraphicsItem类来表示树的节点,并管理它们的位置和连接,以下是实现效果图。 2.实现思路 可以看见,上图所示,我们需要自定义连线类和节点类。 每个节点类Node,需要绘制矩形框体文字…...

go-学习

文章目录 简介标识符字符串的拼接&#xff0c;关键字数据类型声明变量常量算术运算符关系运算符逻辑运算符位运算赋值运算符其他运算符 简介 Go 语言的基础组成有以下几个部分&#xff1a; 1.包声明 2.引入包 3.函数 4.变量 5.语句 & 表达式 6.注释 package main import &q…...

【面试分享】主流编程语言的内存回收机制及其优缺点

以下是几种主流编程语言的内存回收机制及其优缺点&#xff1a; 一、Java 内存回收机制&#xff1a; Java 使用自动内存管理&#xff0c;主要通过垃圾回收器&#xff08;Garbage Collector&#xff0c;GC&#xff09;来回收不再被使用的对象所占用的内存。Java 的垃圾回收器会定…...

STM32-- 串口发送数据

while(USART_GetFlagStatus(USART2,USART_FLAG_TXE)RESET);&#xff1f;&#xff1f; 答&#xff1a; 这行代码&#xff1a; while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) RESET);的作用是等待串口 USART2 的发送数据寄存器&#xff08;TXE&#xff0c;Transmit Dat…...

数据结构 (13)串的应用举例

前言 数据结构中的串&#xff08;String&#xff09;&#xff0c;也称为字符串&#xff0c;是一种常见且重要的数据结构&#xff0c;在计算机科学中被广泛应用于各种场景。 一、文本处理 文本编辑器&#xff1a;在文本编辑器中&#xff0c;字符串被用来表示和存储用户输入的文本…...

qt-- - 版本和下载介绍

qt版本很多&#xff0c;每个大版本都有几个版本是长期支持的&#xff08;LTS&#xff09;&#xff0c;最好使用长期支持的。 例如qt5.15 qt6.2 qt6.8 都是LTS版本的。 qt在线安装需要提供账号&#xff0c;之前安装qt6.8因为账号问题试了很长时间&#xff0c;密码错了。 …...

解锁 Vue 项目中 TSX 配置与应用简单攻略

在 Vue 项目中配置 TSX 写法 在 Vue 项目中使用 TSX 可以为我们带来更灵活、高效的开发体验&#xff0c;特别是在处理复杂组件逻辑和动态渲染时。以下是详细的配置步骤&#xff1a; 一、安装相关依赖 首先&#xff0c;我们需要在命令行中输入以下命令来安装 vitejs/plugin-v…...

ShuffleNet:一种为移动设备设计的极致高效的卷积神经网络

摘要 https://arxiv.org/pdf/1707.01083 我们介绍了一种名为ShuffleNet的计算效率极高的卷积神经网络&#xff08;CNN&#xff09;架构&#xff0c;该架构专为计算能力非常有限的移动设备&#xff08;例如10-150 MFLOPs&#xff09;而设计。新架构利用两种新操作&#xff1a;逐…...

yum源问题的解决方案

linux课堂作业 问题描述 yum 直接安装tree的问题截图 这个错误表明你的系统没有正确注册到 Red Hat Subscription Management&#xff08;这个问题不用管&#xff09;&#xff0c;也没有配置有效的 YUM 软件仓库&#xff0c;因此无法安装或更新软件包。 解决方案&#xff08…...

在Linux中备份msyql数据库和表的详细操作

目录 前情提要 一、备份mysql数据库 原库展示 (一)新建一个数据库 (二)在linux根目录下找个位置暂时存放 (三)临时sql还原真正存放到库中 (四)查看是否备份成功 备份库成功展示 二、备份表的操作 ​编辑 原表emp展示 (一)快速新建一个原结构相同的表 (二)原表所…...

实时数仓Kappa架构:从入门到实战

引言 随着大数据技术的不断发展&#xff0c;企业对实时数据处理和分析的需求日益增长。实时数仓&#xff08;Real-Time Data Warehouse, RTDW&#xff09;应运而生&#xff0c;其中Kappa架构作为一种简化的数据处理架构&#xff0c;通过统一的流处理框架&#xff0c;解决了传统…...

【老白学 Java】Warship v2.0(四)

Warship v2.0&#xff08;四&#xff09; 文章来源&#xff1a;《Head First Java》修炼感悟。 上一篇文章中&#xff0c;老白仔细分析了 v2.0 的设计思路以及实现手段&#xff0c;如果大家有好的设计方案也可以自行尝试。 本篇文章的主要内容是对 Warship 类进行最后的修改&a…...

LLM之学习笔记(一)

前言 记录一下自己的学习历程&#xff0c;也怕自己忘掉了某些知识点 Prefix LM 和 Causal LM区别是什么&#xff1f; Prefix LM &#xff08;前缀语⾔模型&#xff09;和 Causal LM&#xff08;因果语言模型&#xff09;是两者不同类型的语言模型&#xff0c;它们的区别在于生…...

理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端

&#x1f31f; 什么是 MCP&#xff1f; 模型控制协议 (MCP) 是一种创新的协议&#xff0c;旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议&#xff0c;它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...

Linux简单的操作

ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...

2.Vue编写一个app

1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...

镜像里切换为普通用户

如果你登录远程虚拟机默认就是 root 用户&#xff0c;但你不希望用 root 权限运行 ns-3&#xff08;这是对的&#xff0c;ns3 工具会拒绝 root&#xff09;&#xff0c;你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案&#xff1a;创建非 roo…...

苍穹外卖--缓存菜品

1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得&#xff0c;如果用户端访问量比较大&#xff0c;数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据&#xff0c;减少数据库查询操作。 缓存逻辑分析&#xff1a; ①每个分类下的菜品保持一份缓存数据…...

ios苹果系统,js 滑动屏幕、锚定无效

现象&#xff1a;window.addEventListener监听touch无效&#xff0c;划不动屏幕&#xff0c;但是代码逻辑都有执行到。 scrollIntoView也无效。 原因&#xff1a;这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作&#xff0c;从而会影响…...

如何在最短时间内提升打ctf(web)的水平?

刚刚刷完2遍 bugku 的 web 题&#xff0c;前来答题。 每个人对刷题理解是不同&#xff0c;有的人是看了writeup就等于刷了&#xff0c;有的人是收藏了writeup就等于刷了&#xff0c;有的人是跟着writeup做了一遍就等于刷了&#xff0c;还有的人是独立思考做了一遍就等于刷了。…...

Rapidio门铃消息FIFO溢出机制

关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系&#xff0c;以下是深入解析&#xff1a; 门铃FIFO溢出的本质 在RapidIO系统中&#xff0c;门铃消息FIFO是硬件控制器内部的缓冲区&#xff0c;用于临时存储接收到的门铃消息&#xff08;Doorbell Message&#xff09;。…...

Unity UGUI Button事件流程

场景结构 测试代码 public class TestBtn : MonoBehaviour {void Start(){var btn GetComponent<Button>();btn.onClick.AddListener(OnClick);}private void OnClick(){Debug.Log("666");}}当添加事件时 // 实例化一个ButtonClickedEvent的事件 [Formerl…...

华为OD机试-最短木板长度-二分法(A卷,100分)

此题是一个最大化最小值的典型例题&#xff0c; 因为搜索范围是有界的&#xff0c;上界最大木板长度补充的全部木料长度&#xff0c;下界最小木板长度&#xff1b; 即left0,right10^6; 我们可以设置一个候选值x(mid)&#xff0c;将木板的长度全部都补充到x&#xff0c;如果成功…...