Spring Security 6 系列之二 - 基于数据库的用户认证和认证原理
之所以想写这一系列,是因为之前工作过程中使用Spring Security,但当时基于spring-boot 2.3.x,其默认的Spring Security是5.3.x。之后新项目升级到了spring-boot 3.3.0,结果一看Spring Security也升级为6.3.0,关键是其风格和内部一些关键Filter大改,导致在配置同样功能时,多费了些手脚,因此花费了些时间研究新版本的底层原理,这里将一些学习经验分享给大家。
注意:由于框架不同版本改造会有些使用的不同,因此本次系列中使用基本框架是 spring-boo-3.3.0(默认引入的Spring Security是6.3.0),JDK版本使用的是19,所有代码都在spring-security-study项目上:https://github.com/forever1986/spring-security-study.git
目录
- 1 用户读取的基本原理
- 2 基于内存的用户配置
- 3 基于数据库的用户配置
- 3.1 Spring Security默认的JdbcUserDetailsManager
- 3.2 自定义基于数据库的用户配置
- 4 Spring Security认证底层原理
- 5 密码加密方式PasswordEncoder
- 5.1 密码加密原理
- 5.2 DelegatingPasswordEncoder原理
- 5.3 指定PasswordEncoder
上一章中,我们讲了基本入门,spring-boot如何通过默认配置集成Spring Security,也讲了如何自定义配置用户名和密码。在上一章中留下一个问题,就是用户名密码都是配置在项目里面,但实际项目中,我们都是放在数据库中,那么Spring Security如何配置数据库以及其认证原理是如何的,我们在这一章揭晓。
1 用户读取的基本原理
我们先来了解Spring Security如何读取到我们在yml文件中的数据。
1)我们看一下UserDetailsServiceAutoConfiguration这个类,其中有一个inMemoryUserDetailsManager方法
从上图中,我们可以看到,方法inMemoryUserDetailsManager注入了一个Bean,是一个InMemoryUserDetailsManager,其数据来自SecurityProperties(这个类在系列一中讲过,是读取yml或生成默认用户名密码的)。也就是说Spring Security默认构建了一个基于内存的用户密码管理类。
2)我们再看看InMemoryUserDetailsManager继承哪些类或者实现哪些接口。
上图中,我们可以看到InMemoryUserDetailsManager类实现了UserDetailsManager接口,而UserDetailsManager接口继承了UserDetailsService接口。关键点:UserDetailsService接口只有一个loadUserByUsername方法(通过用户名获得UserDetails)。UserDetailsManager接口只是在UserDetailsService接口的基础上增加了对用户的增删改查。
那么可以理解,只需要我们自己实现一个实现UserDetailsService接口或者UserDetailsManager接口的Bean,即可替换原先的配置
3)UserDetails接口,我们看到UserDetailsService接口的loadUserByUsername方法返回一个对象是UserDetails,其也是一个接口。该接口定义了一系列获取用户信息相关的方法
4)而我们在UserDetailsServiceAutoConfiguration这个类中的inMemoryUserDetailsManager方法看到,使用了一个User类创建了一个用户。这个User类其实就是实现了UserDetails接口
User.withUsername(user.getName()).password(getOrDeducePassword(user, passwordEncoder.getIfAvailable())).roles(StringUtils.toStringArray(roles)).build()
5)总结:Spring Security是通过调用UserDetailsService接口的实现类,获得一个UserDetails,里面包括用户信息,可以用于认证的。而Spring Security默认有2个实现类InMemoryUserDetailsManager和JdbcUserDetailsManager,分别对应基于内存的用户认证和基于数据库的用户认证。另外内置一个User类,实现最简单的UserDetails信息。
2 基于内存的用户配置
通过上面对其基本原理的理解,我们知道只需要实现一个UserDetailsService接口的类,就可以替换原来的用户密码配置,下面我们就开始实现。
代码参考lesson02子模块
1)新建一个子模块lesson02,其pom文件引入
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--Spring Boot 提供的 Security 启动器 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency></dependencies>
2)我们只需要新增一个名称service的pakage,在下面新增一个类InMemoryUserDetailsServiceImpl,实现UserDetailsService 接口
@Service
public class InMemoryUserDetailsServiceImpl implements UserDetailsService {@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {return User.withUsername("test").password("{noop}4321").build();}
}
注意:需要将该类设置注解@Service,这样就纳入spring的Bean管理,同时也屏蔽原先默认配置的Bean
密码部分:{noop}4321,前面那个{noop}代表未加密密码,为什么要加入这个东西,后面讲解Spring Security认证流程原理在仔细讲解
3)跟lesson01一样,定义一个demo的controller和启动类
4)访问:http://127.0.0.1:8080/demo 我们可以看见,是使用新的test用户名和4321的密码才能登陆成功,原先控制台或者yml配置都失效了。
3 基于数据库的用户配置
3.1 Spring Security默认的JdbcUserDetailsManager
上面分别讲述了读取用户的原理以及基于内存读取用户的方法。那么基于数据库的用户配置才是今天的主菜。我们看到中Spring Security已经有一个JdbcUserDetailsManager实现类,如果我们要使用该类,需要按照其默认的配置创建表,创建表的语句如下位置:
注意:如果使用默认JdbcUserDetailsManager,可以去看其源码,定义了很多SQL语句,且使用的是传统JdbcTemplate的方式。而在真正项目中,我们一般会引入如mybatis框架,以及用户表会有自己的一些格外信息,所以JdbcUserDetailsManager大部分时候都是不符合我们的要求,因此实际中,我们会自定义自身的用户表以及基于数据库的UserDetailsService。
3.2 自定义基于数据库的用户配置
前提条件:基于mysql数据库创建一个数据库,名为spring_security_study,创建用户表t_user
-- spring_security_study.t_user definition
CREATE TABLE `t_user` (`id` bigint NOT NULL AUTO_INCREMENT,`username` varchar(100) NOT NULL,`password` varchar(100) NOT NULL,`email` varchar(100) DEFAULT NULL,`phone` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;INSERT INTO spring_security_study.t_user (username, password, email, phone)VALUES('test', '{noop}1234','test@test.com','13788888888');
下面开始说明基于自定义数据库的用户配置
代码参考lesson03子模块
1)新建子模块lesson03,其pom引入以下依赖:(引入mybatis-plus、mysql-connector、druid连接池、lombok)
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--Spring Boot 提供的 Security 启动器 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-spring-boot3-starter</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId></dependency>
</dependencies>
2)在resources下面创建yaml文件,配置数据库连接和mybatis-plus相关配置
server:port: 8080
spring:# 配置数据源datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://127.0.0.1:3306/spring_security_study?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=trueusername: rootpassword: rootdruid:initial-size: 5min-idle: 5maxActive: 20maxWait: 3000timeBetweenEvictionRunsMillis: 60000minEvictableIdleTimeMillis: 300000validationQuery: select 'x'testWhileIdle: truetestOnBorrow: falsetestOnReturn: falsepoolPreparedStatements: falsefilters: stat,wall,slf4jconnectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000;socketTimeout=10000;connectTimeout=1200mybatis-plus:global-config:banner: falsemapper-locations: classpath:mappers/*.xmltype-aliases-package: com.demo.lesson03.entityconfiguration:cache-enabled: falselocal-cache-scope: statement
3)创建package:entity和mapper,分别定义TUser和TUserMapper,读取数据库用户数据。
@Data
public class TUser {@TableId(type = IdType.ASSIGN_ID)private Long id;private String username;private String password;private String email;private String phone;
}
TUserMapper定义个通过用户名获取用户的方法
@Mapper
public interface TUserMapper {// 根据用户名,查询用户信息@Select("select * from t_user where username = #{username}")TUser selectByUsername(String username);}
4)新建package:service,新建一个JdbcUserDetailsServiceImpl,通过mapper获取用户并返回
@Service
public class JdbcUserDetailsServiceImpl implements UserDetailsService {@Autowiredprivate TUserMapper tUserMapper;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {// 查询自己数据库的用户信息TUser user = tUserMapper.selectByUsername(username);if(user == null){throw new UsernameNotFoundException(username);}return User.builder().username(user.getUsername()).password(user.getPassword()).roles("USER").build();}}
5)和lesson02一样定义一个demo的controller接口和启动类SecurityLesson03Application,并启动
6)访问:http://127.0.0.1:8080/demo 我们可以看见,是使用新的test用户名和1234的密码才能登陆成功,原先控制台或者yml配置都失效了。
4 Spring Security认证底层原理
上面讲解了如何通过内存或者数据库配置登录用户信息,那么Spring Security是如何做用户认证的呢?
首先我们要先了解到Spring Security是一系列的Filter过滤器组成的链路,每个过滤器处理器对应的功能,如果符合则往下走,不符合则返回。关于这部分,我们在下一章中在着重讲。
之前说过Spring Security支持的认证有多种,还可以自定义认证。我们这里以用户名和密码的认证方式为例,讲解一下其主要的原理。我们主要关注Spring Security的认证过滤器(UsernamePasswordAuthenticationFilter)。
1)我们可以debug一下HttpSecurity下面这行代码,里面可以看到Spring Security默认配置定义了哪些Filter过滤器。
2)我们可以截图看到以下默认配置下有16个过滤器(不同Security版本可能不同,我这6.3.0默认什么都不配置是16),其过滤器的各自用途我们下一章讲。这一章我们主要看UsernamePasswordAuthenticationFilter
3)UsernamePasswordAuthenticationFilter是继承AbstractAuthenticationProcessingFilter,我们看看AbstractAuthenticationProcessingFilter的doFilter方法,里面有2个关键内容,一个是调用attemptAuthentication方法做认证(这个方法具体实现是在UsernamePasswordAuthenticationFilter),一个successfulAuthentication方法做后续认证成功处理(包括保存SecurityContext,调用securityContextRepository存储session)
4)我们回到认证流程,刚才说AbstractAuthenticationProcessingFilter调用attemptAuthentication,其实就是调用UsernamePasswordAuthenticationFilter里面的attemptAuthentication方法就是执行关键。看下图解释attemptAuthentication的流程
5)上图的最后一步验证,其实是调用AuthenticationManager的authenticate方法,AuthenticationManager是一个接口,该接口实现类有几个,Spring Security默认提供ProviderManager
6)ProviderManager的authenticate方法中,真正认证的是result = provider.authenticate(authentication);这一句,而provider是一个叫AuthenticationProvider接口
7)AuthenticationProvider接口,Spring Security默认提供抽象实现类AbstractUserDetailsAuthenticationProvider,我们关注其authenticate方法,方法里面有2个调用值得我们注意,一个是retrieveUser(这个类获得用户信息)和一个additionalAuthenticationChecks(做用户认证)。
8)我们可以看到retrieveUser和additionalAuthenticationChecks方法在AbstractUserDetailsAuthenticationProvider只是一个定义,还需要其子类实现,而Spring Security默认提供DaoAuthenticationProvider实现类
从上图我们就可以理解,为什么我们实现UserDetailsService接口,就能够修改用户信息的来源(比如说数据库),因为在retrieveUser方法中就是调用UserDetailsService接口去获取用户信息。
另外一个就是用户认证additionalAuthenticationChecks方法,通过一个PasswordEncoder去做用户密码匹对。
9)总结:通过UsernamePasswordAuthenticationFilter过滤器做认证,UsernamePasswordAuthenticationFilter调用AuthenticationManager管理器的authenticate方法,而AuthenticationManager是调用AuthenticationProvider的authenticate方法,AuthenticationProvider是通过其实现类DaoAuthenticationProvider提供最终的认证。如下流程图:
5 密码加密方式PasswordEncoder
5.1 密码加密原理
我们在前面分析认证原理中,DaoAuthenticationProvider的additionalAuthenticationChecks方法就是实现其密码匹配的。我们可以看到里面有一个PasswordEncoder。从名字来看就是密码加密
Spring Security提供了PasswordEncoder接口,并通过实现该接口内置很多密码加密验证方式,比如不加密、对称加密、非对称加密甚至可以自定义。这里我们着重了解3个实现类NoOpPasswordEncoder、BCryptPasswordEncoder和DelegatingPasswordEncoder
上图中就是3个常用的内置加密方式,他们分别代表NoOpPasswordEncoder(不加密)、BCryptPasswordEncoder(使用SHA-256 +随机盐+密钥对密码进行加密)、DelegatingPasswordEncoder(代理类,通过其前缀判断不同加密方式)。Spring Security默认情况下,采用的是DelegatingPasswordEncoder,下面我们了解一下DelegatingPasswordEncoder。
5.2 DelegatingPasswordEncoder原理
DelegatingPasswordEncoder可以通过给密码前面增加一个{前缀},同时支持多种密码加密验证,而Spring Security默认的方式就是DelegatingPasswordEncoder。下图就是不同加密方式的前缀。
注意:前面我们在密码之前增加{noop},其实就是采用不加密方式,当然这是非常不安全的,只有在演示中使用
5.3 指定PasswordEncoder
你可以指定自己的密码加密方式,只需要继承PasswordEncoder接口,并实现其方法即可。比如指定BCryptPasswordEncoder
@Bean
public BCryptPasswordEncoder createPasswordEncoder(){return new BCryptPasswordEncoder();
}
在实际项目中,密码一定是要加密,且最好不可逆和难以破解的方式,比如SHA-256。
结语:至此,我们终于将Spring Security的认证底层原理讲了一遍。如果不了解的朋友,可以多debug几次就能够明白其中原理。到目前为止,我们只是配置了用户读取方式,其它的Spring Security配置都还是默认的,我们也可以看到默认情况下,Spring Security就加载了16个过滤器,说明有很多功能还没有讲,那么下一章,就是了解Spring Security底层原理以及常见Filter过滤的作用。
相关文章:
Spring Security 6 系列之二 - 基于数据库的用户认证和认证原理
之所以想写这一系列,是因为之前工作过程中使用Spring Security,但当时基于spring-boot 2.3.x,其默认的Spring Security是5.3.x。之后新项目升级到了spring-boot 3.3.0,结果一看Spring Security也升级为6.3.0,关键是其风…...
vue中打包dist文件内static 和 assets 的区别
背景 在Vue.js项目中,assets 和 static 是两个用于存放静态资源的文件夹,但它们在使用方式和处理机制上有所不同 用途 assets: assets 文件夹通常用于存放那些需要在构建过程中被Webpack处理的静态资源。这些资源可以包括图片、字体、样式文件&#…...
Big Model weekly | 第49期
点击蓝字 关注我们 AI TIME欢迎每一位AI爱好者的加入! 01 Magnetic Preference Optimization: Achieving Last-iterate Convergence for Language Models Alignment 自我对弈方法在多个领域增强模型能力方面展现出了显著的成功。在基于人类反馈的强化学习࿰…...
Node.js内置模块
1.内置模块 Node.js的中文网参考手册:https://nodejs.cn//api 帮助文档 API文档:查看对应的模块,左边是模块,右边是模块的成员 源码:https://github.com/nodejs/node/tree/main/lib 查看 例如: http.js 创建web服务器的模块 -->进入源码中,搜索…...
使用Nexus3搭建npm私有仓库
一、npm介绍 npm的全称是Node Package Manager,它是一个开放源代码的命令行工具,用于安装、更新和管理Node.js模块。npm是Node.js的官方模块管理器,它允许用户从一个集中的仓库中下载和安装公共的Node.js模块,并将这些模块集成到…...
MySQL学习之表操作
MySQL学习之表操作 基础命令 查询当前所在数据库的所有表 show tables;查看指定的表结构, 可以查看到指定表的字段,字段的类型、是否可以为NULL,是否存在默认值等信息 mysql> desc user; ----------------------------------------------- | Field |…...
C语言学习day22:ReadProcessMemory函数/游戏内存数据读取工具开发
简言: ReadProcessMemory函数是 Windows API 中的一个函数,用于从目标进程的虚拟内存空间中读取数据。这个函数非常有用,尤其是在进行内存分析、调试、或某些类型的逆向工程时。 ReadProcessMemory函数 函数原型 BOOL ReadProcessMemory(…...
Linux虚拟文件系统
参考:深入分析LINUX内核源码 深入分析Linux内核源码 (kerneltravel.net) 作为一个最著名的自由软件,Linux 确实名不虚传,几乎处处体现了“自由”,你可以编译适合自己系统要求的内核,或者轻松添加别人开发的新的模块。只…...
OpenIPC开源FPV之Adaptive-Link天空端代码解析
OpenIPC开源FPV之Adaptive-Link天空端代码解析 1. 源由2. 框架代码3. 报文处理3.1 special报文3.2 普通报文 4. 工作流程4.1 Profile 竞选4.2 Profile 研判4.3 Profile 应用 5. 总结6. 参考资料7. 补充资料7.1 RSSI 和 SNR 的物理含义7.2 信号质量加权的理论依据7.3 实际应用中…...
NPU是什么?电脑NPU和CPU、GPU区别介绍
随着人工智能技术的飞速发展,计算机硬件架构也在不断演进以适应日益复杂的AI应用场景。其中,NPU(Neural Processing Unit,神经网络处理器)作为一种专为深度学习和神经网络运算设计的新型处理器,正逐渐崭露头…...
.Net WebAPI(一)
文章目录 项目地址一、WebAPI基础1. 项目初始化1.1 创建简单的API1.1.1 get请求1.1.2 post请求1.1.3 put请求1.1.4 Delete请求 1.2 webapi的流程 2.Controllers2.1 创建一个shirts的Controller 3. Routing3.1 使用和创建MapControllers3.2 使用Routing的模板语言 4. Mould Bind…...
.NET 技术 | 调用系统API创建Windows服务
01阅读须知 此文所提供的信息只为网络安全人员对自己所负责的网站、服务器等(包括但不限于)进行检测或维护参考,未经授权请勿利用文章中的技术资料对任何计算机系统进行入侵操作。利用此文所提供的信息而造成的直接或间接后果和损失…...
OpenLinkSaas 2025年1月开发计划
先来看看OpenLinkSaas的大目标 在OpenLinkSaas的产品目标中,让开发人员更加方便的使用云资源是目标之一。通过各大云厂商的API,来可视化云上基础设施的数据是远远不够的。我们准备在2025年1月份增加方便管理和运营研发场景下服务器的能力。 这部分的功能…...
同态加密算法详解及Python实现
目录 同态加密算法详解及Python实现第一部分:同态加密概述与原理1.1 什么是同态加密?同态加密的定义:1.2 同态加密的分类1.3 同态加密的优势与挑战优势挑战第二部分:常见同态加密算法及其应用场景2.1 RSA同态加密支持操作应用场景2.2 Paillier加密支持操作应用场景2.3 Gent…...
【HarmonyOS NEXT】ArkTs函数、类、接口、泛型、装饰器解析与使用
1. 前置学习文档 【HarmonyOS NEXT】ArkTs数据类型解析与使用(https://juejin.cn/spost/7448894500348608522) 2. 前言 在原生JavaScript中只有函数和类的实现,为了更好的面向对象编程,TypeScript 引入了接口、泛型、装饰器等特性。ArkTS也继承了这些特性…...
【数学】矩阵的逆与伪逆 EEGLAB
文章目录 前言matlab代码作用EEGLAB 中的代码总结参考文献 前言 在 EEGLAB 的使用中,运行程序时出现了矩阵接近奇异值,或者缩放错误。结果可能不准确。RCOND 1.873732e-20 的 bug,调查 EEGLAB 后发现是 raw 数据的问题。 matlab代码 A_1 …...
用github镜像加速, --recursive还是去github站怎么处理?
小伙伴们大多碰到过github抽风的情况,时通时断,时快时慢,非常考验心情。 以前碰到连不上的时候,我大多就是在gitee和gitcode网站找一下镜像,找到后直接git clone 新地址即可。但是碰到 --recursive的时候就不行了&…...
第P2周:Pytorch实现CIFAR10彩色图片识别
🍨 本文为🔗365天深度学习训练营 中的学习记录博客🍖 原作者:K同学啊 目标 实现CIFAR-10的彩色图片识别实现比P1周更复杂一点的CNN网络 具体实现 (一)环境 语言环境:Python 3.10 编 译 器: …...
springboot3访问第三方接口
添加依赖(如果尚未添加) 在pom.xml文件中,确保已经包含spring-boot-starter-web依赖,因为RestTemplate通常在这个依赖范围内。如果没有,添加如下依赖: <dependency><groupId>org.springframe…...
Ubuntu K8s
https://serious-lose.notion.site/Ubuntu-K8s-d8d6a978ad784c1baa2fc8c531fbce68?pvs74 2 核 2G Ubuntu 20.4 IP 172.24.53.10 kubeadmkubeletkubectl版本1.23.01.23.01.23.0 kubeadm、kubelet 和 kubectl 是 Kubernetes 生态系统中的三个重要组件 kubeadm: 主…...
大数据第三次周赛
类斐波那契循环数 #include<bits/stdc.h> using namespace std; #define int long long int arr[1000010]; bool key(int k){int num0;string strto_string(k);for(int i0;i<str.length();i){arr[num]str[i]-0;}int l0,rnum-1;int shix0; while(shix<k){shix0;for…...
《Java核心技术I》Swing用户界面组件
Swing和模型-视图-控制器设计模式 用户界面组件各个组成部分,如按钮,复选框,文本框或复杂的树控件,每个组件都有三个特征: 内容,如按钮的状态,文本域中的文本。外观,颜色,…...
Web开发 -前端部分-CSS
CSS CSS(Cascading Style Sheet):层叠样式表,用于控制页面的样式(表现)。 一 基础知识 1 标题格式 标题格式一: 行内样式 <!DOCTYPE html> <html lang"en"><head><meta…...
旅游系统旅游小程序PHP+Uniapp
旅游门票预订系统,支持景点门票、导游产品便捷预订、美食打卡、景点分享、旅游笔记分享等综合系统 更新日志 V1.3.0 1、修复富文本标签 2、新增景点入驻【高级版本】3、新增门票核销【高级版】4、新增门票端口【高级版】...
Windows 与 Linux 下 Ping IPv6 地址 | 常用网络命令
注:本文为网络命令相关文章合辑。 未整理去重。 一、IPv6 概述 IPv6 即 “Internet 协议版本 6”,因 IPv4 地址资源面临耗尽问题而被引入以替代 IPv4。IPv6 则提供了理论上多达 2 128 2^{128} 2128 个地址,有效解决地址不足困境。 IPv6 具…...
前端学习一
一 进程与线程 线程是进程执行的最小单位,进程是系统分配任务的最小单位。 一个进程可执行最少一个线程。线程分为子线程和主线程。 主线程关闭则子线程关闭。 二 浏览器进程 浏览器是多进程多线程应用。 进程包括: 浏览器进程 负责程序交互渲染…...
【Python · PyTorch】卷积神经网络(基础概念)
【Python PyTorch】卷积神经网络 CNN(基础概念) 0. 生物学相似性1. 概念1.1 定义1.2 优势1.2.1 权重共享1.2.2 局部连接1.2.3 层次结构 1.3 结构1.4 数据预处理1.4.1 标签编码① One-Hot编码 / 独热编码② Word Embedding / 词嵌入 1.4.2 归一化① Min-…...
Spring Framework 路径遍历漏洞复现(CVE-2024-38819)
hu0x01 产品描述: Spring Framework 是一个功能强大的 Java 应用程序框架,旨在提供高效且可扩展的开发环境。它结合了轻量级的容器和依赖注入功能,提供了一种使用 POJO 进行容器配置和面向切面的编程的简单方法,以及一组用于AOP的模块。0x02 漏洞描述: Spring Framework 存…...
心法利器[122] | 算法面试的八股和非八股讨论
心法利器 本栏目主要和大家一起讨论近期自己学习的心得和体会。具体介绍:仓颉专项:飞机大炮我都会,利器心法我还有。 2023年新的文章合集已经发布,获取方式看这里:又添十万字-CS的陋室2023年文章合集来袭,更…...
实操给自助触摸一体机接入大模型语音交互
本文以CSK6 大模型开发板串口触摸屏为例,实操讲解触摸一体机怎样快速增加大模型语音交互功能,使用户能够通过语音在一体机上查询信息、获取智能回答及实现更多互动功能等。 在本文方案中通过CSK6大模型语音开发板采集用户语音,将语音数据传输…...
AJAX家政系统自营+多商家家政系统服务小程序PHP+Uniapp
一款同城预约、上门服务、到店核销家政系统,用户端、服务端、门店端各端相互依赖又相互独立,支持选择项目、选择服务人员、选择门店多种下单方式,支持上门服务和到店核销两种服务方式,支持自营和多商家联营两种运营模式࿰…...
LiveData源码研究
LiveData 源码分析 前言 用过MVVM的大概知道LiveData可以感知组件的生命周期,当页面活跃时,更新页面数据, 当页面处于非活跃状态,它又会暂停更新,还能自动注册和注销观测者,能有效避免内存泄漏和不必要的…...
Root软件学习
一、命令行输入下方命令打开root文件 root filename.root 二、在root命令行下输入.help查看root下可用的指令 .help输入.q是退出root命令行 .q 三、输入下方指令查看当前打开的root文件的目录 .ls 四、打印Hits树下的内容(print) 方框里是各种树文…...
研发文档管理系统:国内外9大选择比较
文章主要对比了9款国内外研发文档管理系统:1.PingCode; 2. Worktile; 3. 飞书; 4. 石墨文档; 5. 腾讯文档; 6. 蓝湖; 7. Confluence; 8. Notion; 9. Slab。 在企业研发过…...
centos 7.9 freeswitch1.10.9环境搭建
亲测版本centos 7.9系统–》 freeswitch1.10.9 一、下载插件 yum install -y git alsa-lib-devel autoconf automake bison broadvoice-devel bzip2 curl-devel libdb4-devel e2fsprogs-devel erlang flite-devel g722_1-devel gcc-c++ gdbm-devel gnutls-devel ilbc2...
嵌入式驱动开发详解17(CAN驱动开发)
文章目录 前言CAN简介CAN收发器CAN协议讲解电气特性传输协议数据帧遥控帧错误帧过载帧帧间隔 同步矫正 CAN控制器CAN控制器模式CAN接收器CAN波特率 CAN设备树分析CAN测试后续参考文献 前言 该专栏主要是讲解嵌入式相关的驱动开发,但是由于部分模块的驱动框架过于复…...
探索 Janus-1.3B:一个统一的 Any-to-Any 多模态理解与生成模型
随着多模态技术的不断发展,越来越多的模型被提出以解决跨文本与图像等多种数据类型的任务。Janus-1.3B 是由 DeepSeek 推出的一个革命性的模型,它通过解耦视觉编码并采用统一的 Transformer 架构,带来了一个高度灵活的 any-to-any 多模态框架…...
黑马头条day01 微服务搭建
1.请求调用流程 如http://localhost:8803/static/js/2.0195d7180dc783c3fe99.js这种静态资源,采用http的发送到本地8803端口的静态资源请求,而nginx配置的监听8801、8802、8803,所以请求走到nginx,nginx的admin配置文件 upstream…...
AI辅助编程工具对比:Cursor AI、Windsurf AI 和 GitHub Copilot
功能和特性 1. Cursor AI 基于VS Code构建,集成了GPT-4等多个AI模型,提供高级智能支持。支持AI代码补全、错误修正以及通过自然语言执行命令。具备多文件编辑和上下文理解能力,能够在复杂项目中提供跨文件的智能建议。提供标签功能…...
【Qt】qt安装
在工作一年之后,还是想做一个Qt的教程,遥想研一刚刚接触Qt,从0到1学习,没有什么参考书籍,网上的资料也不多,幸好Qt官方文档写得好,加上自己肯研究,才堪堪入门。 现在我想自己写一个…...
课设项目十:智能手电筒(使用金沙滩51单片机)
00 题目介绍 功能: 硬件设置: 使用51单片机连接光敏传感器、LED灯和手电筒开关按钮。 环境感知: 实时监测周围光照强度。 LED控制: 根据光照强度自动控制LED灯的开关。 手动控制: 提供手电筒开关按钮,…...
Oracle中COUNT函数对NULL和空字符串的处理方式
Oracle中,使用COUNT函数的时候,COUNT()和COUNT(null)得到的结果都是0,也就是说,如果我们COUNT中选择的那列属性中为null的或者的那行是不会被计数的。MySQL中count(null)效果和Oracle中一样,但是count()能正常计数。 在…...
OpenHarmony和OpenVela的技术创新以及两者对比
两款有名的国内开源操作系统,OpenHarmony,OpenVela都非常的优秀。本文对二者的创新进行一个简要的介绍和对比。 一、OpenHarmony OpenHarmony具有诸多有特点的技术突破和重要贡献,以下是一些主要方面: 架构设计创新 分层架构…...
Windows常用命令
该篇文章是博主不断从工作中总结而来,会持续不断更新 文件和目录管理命令 列出指定目录中的文件和子目录:dir 路径 更改当前工作目录:cd 路径 创建新目录:mkdir 目录名 删除空目录:rmdir 目录名 删除指定文件…...
牛客网 SQL2查询多列
SQL2查询多列 select device_id,gender,age,university //查询哪些字段 from user_profile //从哪个表中查找 每日问题 C 中面向对象编程如何处理异常? 在C中,面向对象编程(OOP)处理异常主要通过异常处理机制来实现。C 提供了…...
容器,网络基础
小结: 1、利用网桥和虚拟网卡 2、利用Veth Pair虚拟设备,一个网卡可以直接出现在另外一个网卡中 一个Linux容器能看见的“网络栈”,实际上是被隔离在它自己的Network Namespace当中的 “网络栈”,就包括了:网卡&#…...
Treap树堆【东北大学oj数据结构8-4】C++
题面 二叉搜索树会因为插入的数据的值可能变得不平衡,搜索/插入/删除操作的效率变得低效。例如,如果依次插入 n 个升序的数据,则树将看起来像一个列表,其高度将为 n,并且查询时间变得很长。一个解决策略是随意打乱要插…...
基于STM32的智电表系统课题设计思路:python友好界面、ADC、UART串口、数据分析
1. 项目选题与需求分析 1.1 选题背景和动机 随着社会的快速发展,电力的消耗不断增加,如何高效管理和监测用电成为了一个重要的课题。传统的电表只能提供简单的用电计量,无法满足现代家庭和工业对用电数据实时监控、远程控制及数据分析的需求…...
博弈论1:拿走游戏(take-away game)
假设你和小红打赌,玩“拿走游戏”,输的人请对方吃饭.... 你们面前有21个筹码,放成一堆;每轮你或者小红可以从筹码堆中拿走1个/2个/3个;第一轮你先拿,第二轮小红拿,你们两个人交替进行;拿走筹码堆…...
【人工智能解读】神经网络(CNN)的特点及其应用场景器学习(Machine Learning, ML)的基本概念
前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默, 忍不住分享一下给大家。点击跳转到网站 学习总结 1、掌握 JAVA入门到进阶知识(持续写作中……) 2、学会Oracle数据库入门到入土用法(创作中……) 3、手把…...