简单的开始

创建SpringBoot项目

首先创建一个简单的springboot项目,假设端口为8888,添加controller控制层,并在其中添加TestController控制类,那么启动springboot项目之后,访localhost:8888/api/message页面会显示my first message

@RestController
@RequestMapping("/api")
public TestController{@GetMapping("/messages")public String myMessage(){return "my first message";}
}

添加SpringSecurity的依赖

<dependencies><!-- ... 其他依赖元素 ... --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId><!--可以通过下面的内容进行版本指定--><spring-security.version>6.2.0-SNAPSHOT</spring-security.version></dependency>
</dependencies>

SpringSecurity认证登录

运行springboot项目后,在控制台输出窗口出现:

Using generated security password: 8e557245-73e2-4286-969a-ff57fe326336

尝试访问localhost:port任意后端接口地址,可以发现出现了登录窗口,

使用user: user password:8e557245-73e2-4286-969a-ff57fe326336 这里的密码就是控制台输出的密码。

这就是springsecurity的端口认证机制。

原理说明

Filter和FilterChain

当客户端向应用程序发送请求时,SpringSecurity会创建一系列的Filter来过滤请求,这样的Filter有多个,这些Filter构成了从客户端到Servlet的一个FilterChain,在通过FilterChain的过滤之后,这个请求才会被Servlet处理。

需要注意的是Filter 会影响下游的 Filter 实例,当匹配到一个Filter之后就不再匹配下面的Filter

流程如下所示。

filterchain

过滤过程的伪代码

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {// 在过滤之前的动作chain.doFilter(request, response); // 进行过滤// 过滤之后的动作
}

FilterChainProxy和SecurityFilterChain

基本流程

当一个请求来临时,我们常常会有这样的动作,对于某一个请求接口,去查看对应的过滤器,比如对account相关的接口,我们就会给account制定对应的过滤器,当account相关的请求来临时,我们必然的就需要去通过account的过滤器去处理该请求。

FilterChainProxy就是这样的一个角色, 用来确定当前请求应该调用哪些 Spring Security Filter 实例。

SecurityFilterChain的作用就是将过滤器进行分类,用来被FilterChainProxy识别调用。

securityfilterchain

因此当设计多个接口过滤器时,基本架构如下图所示

multi securityfilterchain

举例说明

FilterChainProxy 决定应该使用哪个 SecurityFilterChain。只有第一个匹配的 SecurityFilterChain 被调用。

  • 如果请求的URL是 /api/messages,它首先与 /api/**SecurityFilterChain0 模式匹配,所以只有 SecurityFilterChain0 被调用,尽管它也与 SecurityFilterChainn 匹配。
  • 如果请求的URL是 /messages,它与 /api/**SecurityFilterChain_0 模式不匹配,所以 FilterChainProxy 会继续顺序尝试下面的 SecurityFilterChain。假设没有其他 SecurityFilterChain 实例相匹配,则调用 SecurityFilterChain_n

工作流程

客户端请求受保护资源
Spring Security 过滤器链拦截请求
UserDetailsServiceAutoConfiguration 配置 AuthenticationManager
需要认证?
UsernamePasswordAuthenticationFilter 拦截
访问资源
DaoAuthenticationProvider 处理认证
使用自定义 UserDetailsService?
自定义 UserDetailsService 加载用户信息
InMemoryUserDetailsManager 加载默认用户信息
密码匹配?
创建 Authentication 对象
抛出 AuthenticationException
将 Authentication 对象放入 SecurityContextHolder
访问资源
重定向到登录页面或返回错误

1. 自动配置的过程

  1. UserDetailsServiceAutoConfiguration 类上的条件注解

    • @ConditionalOnClass(AuthenticationManager.class)

      ​ 确保 AuthenticationManager 类在类路径上。

    • @ConditionalOnBean(ObjectPostProcessor.class)

      ​ 确保 Spring 容器中存在 ObjectPostProcessor 的 Bean

    • @ConditionalOnMissingBean({ AuthenticationManager.class, AuthenticationProvider.class, UserDetailsService.class })

      确保 Spring 容器中没有定义 AuthenticationManagerAuthenticationProviderUserDetailsService 的 Bean。

  2. 默认InMemoryUserDetailsManager 的Bean 的创建

    如果上述条件都满足,UserDetailsServiceAutoConfiguration 会创建一个 InMemoryUserDetailsManager 的 Bean 作为默认的用户详细信息服务管理器。这个管理器会在内存中创建一个用户,通常用户名为 “user”,密码为随机生成的 UUID,这个角色为 “USER”。

    在创建 InMemoryUserDetailsManager 时,UserDetailsServiceAutoConfiguration 会检查 SecurityProperties 中定义的用户密码。如果密码是生成的,它会记录一条日志,显示使用的密码。同时,它还会检查密码是否已经使用某种算法进行了编码,如果没有,它会使用 {noop} 前缀,表示密码没有被编码。

    创建过程代码:可以简单浏览,之后自己创建配置时会借鉴到

        @Beanpublic InMemoryUserDetailsManager inMemoryUserDetailsManager(SecurityProperties properties,// springsecurity的配置文件,里面有默认的用户名,可以进入 SecurityProperties 查看详细数据ObjectProvider<PasswordEncoder> passwordEncoder) // 密码编码器{SecurityProperties.User user = properties.getUser(); // 配置文件中的用户List<String> roles = user.getRoles(); // 获取角色return new InMemoryUserDetailsManager(new UserDetails[]{User.withUsername(user.getName()) // 账号.password(		// 密码this.getOrDeducePassword(user, (PasswordEncoder)passwordEncoder.getIfAvailable())).roles(StringUtils.toStringArray(roles)) //角色.build()});}
  3. 注册Bean

    最终,UserDetailsServiceAutoConfiguration 会将 InMemoryUserDetailsManager 注册为 Spring 应用上下文中的一个 Bean,这样 Spring Security 在认证时就可以使用这个默认的用户详细信息服务。

2. UserDetailsService的作用

我们通过上面InMemoryUserDetailsManager的类,可以分析得出

  • InMemoryUserDetailsManager实现了UserDetailsManager、UserDetailsPasswordService中的方法

  • UserDetailsManager继承UserDetailsService

    在这里插入图片描述

因此InMemoryUserDetailsManager的关键就是UserDetailsManagerUserDetailsPasswordService以及实现自UserDetailsService中的方法

接口方法描述
UserDetailsManagervoid createUser(UserDetails user)根据提供的用户详情创建一个新用户账号
void updateUser(UserDetails user)更新指定的用户账号
void deleteUser(String username)从系统中删除具有给定登录名的用户账号
void changePassword(String oldPassword, String newPassword)修改用户账号的密码。这应该在持久的用户存储库中更改用户的密码(数据库、LDAP等)
boolean userExists(String username)检查具有给定登录名的用户账号是否存在于系统中
UserDetails loadUserByUsername(String username)根据用户名加载用户信息,此方法从 UserDetailsService 继承
UserDetailsPasswordServiceUserDetails updatePassword(UserDetails user, String newPassword)更新用户密码。在用户登录成功后,如果检测到密码需要更新(例如,密码策略变更),则调用此方法

​ 而我们可以通过上面部分自动配置过程可以知道,假如Spring 容器中定义了 AuthenticationManagerAuthenticationProviderUserDetailsService 的 Bean,那么自动配置文件将不会生效。

3. AuthenticationManager的作用

在Spring Security中,AuthenticationManager 是一个核心接口,负责对用户的认证请求进行处理。它定义了一个 authenticate 方法,该方法接受一个 Authentication 对象作为参数,并返回一个完全认证过的 Authentication 对象。如果认证失败,则抛出 AuthenticationException

ProviderManagerAuthenticationManager 的一个常见实现,它使用一个 AuthenticationProvider 列表来处理认证请求。每个 AuthenticationProvider 都有机会对认证请求进行处理,如果一个 AuthenticationProvider 无法处理请求,ProviderManager 会尝试下一个。这个过程会一直持续,直到找到一个能够成功认证请求的 AuthenticationProvider,或者所有的 AuthenticationProvider 都尝试完毕。

找到匹配的 Provider
认证成功
认证失败
未找到匹配的 Provider
开始认证
AuthenticationManager
ProviderManager
遍历 AuthenticationProvider 列表
Provider 进行认证
返回认证后的 Authentication 对象
抛出 AuthenticationException
抛出 ProviderNotFoundException
认证完成

4. 手动配置账号密码

1)创建配置类、用户管理器

因此我们创建自己的WebSecurityConfig类 ,在里面进行InMemoryUserDetailsManager的注入,并实现构造方法。这样我们就手动创建了自己的配置内容。

@Configuration
public class WebSecurityConfig {@Beanpublic InMemoryUserDetailsManager inMemoryUserDetailsManager() {return new InMemoryUserDetailsManager();}
}

当然我们里面还没有给InMemoryUserDetailsManager添加任何用户。

2)初始化用户

添加下面代码,在创建InMemoryUserDetailsManager时新建一个用户

@Configuration
public class WebSecurityConfig {@Beanpublic InMemoryUserDetailsManager inMemoryUserDetailsManager() {return new InMemoryUserDetailsManager(User.withUsername("user") // 用户名.password("{noop}password") // 密码,以{noop}开头的话代表不加密.roles("a") // 使用可变参数传递角色.build());}
}

这样,当我们启动时,就可以根据上面的账号和密码进行登录

3)添加用户

当然我们也可以通过调用InMemoryUserDetailsManager中的createUser方法添加用户的方式,来初始化manager用户管理器,下面我们展示创建两个用户的过程。

@Configuration
public class WebSecurityConfig {@Beanpublic InMemoryUserDetailsManager inMemoryUserDetailsManager() {InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();manager.createUser(new User("admin", "{noop}123456", List.of(new SimpleGrantedAuthority("ROLE_ADMIN"))));manager.createUser(new User("user", "{noop}654321", List.of(new SimpleGrantedAuthority("ROLE_USER"))));return manager;}
}
4)认证过程

我们通过上面的内容已经知道了初始化用户 添加用户,同样的里面的updateUser deleteUser changePassword userExists方法也基本类似,不再赘述。

接下来需要弄懂的就是如何认证的呢,我们明明没有写这些相关的方法。

通过最开始的流程图,我们可以知道在配置好认证用户之后,之后程序对于每一个请求都会进行拦截。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

请求拦截

AbstractAuthenticationProcessingFilter将请求拦截,并通过调用attemptAuthentication方法进行处理,而这个方法的具体实现存在于UsernamePasswordAuthenticationFilter

将请求进行拦截,然后交给授权管理器AuthenticationManager进行控制
在这里插入图片描述

授权管理器认证

进入authenticate()方法发现进入到一个AuthenticationManager接口中,而这个接口的实现类是ProviderManager

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

ProviderManager类中的authenticate方法委派认证工作给一个或多个AuthenticationProvider

验证用户是否存在

AuthenticationProvider仍为一个接口,其默认实现类为AbstractUserDetailsAuthenticationProvider

AbstractUserDetailsAuthenticationProviderauthenticate方法流程如下:

  • 开始认证:认证过程开始。
  • 检查Authentication类型:确保传入的Authentication对象是UsernamePasswordAuthenticationToken类型。
  • 抛出异常:如果类型不匹配,抛出异常。
  • 确定用户名:从Authentication对象中获取用户名。
  • 从Cahce缓存获取UserDetails:尝试从Cahce缓存中获取UserDetails对象。
  • Cahce未命中:如果Cahce未命中,从用户信息源(如数据库)检索用户信息。
  • 用户不存在:如果用户不存在,根据配置抛出UsernameNotFoundExceptionBadCredentialsException
  • 用户存在:如果用户存在,校验用户状态(如账户是否过期、是否锁定等)。
  • 用户状态无效:如果用户状态无效,抛出AuthenticationException
  • 执行额外的认证检查:执行任何额外的认证检查(如密码过期检查)。
  • 认证检查失败:如果认证检查失败,重新检索用户信息并再次执行检查。
  • 执行后置认证检查:执行认证成功后的后置检查。
  • 后置检查失败:如果后置检查失败,抛出AuthenticationException
  • 检查是否使用缓存:检查认证过程中是否使用了缓存。
  • 使用了缓存:如果没有使用缓存,将用户信息放入缓存。
  • 创建认证成功的Authentication对象:创建一个新的Authentication对象,表示认证成功。
  • 返回认证成功的Authentication对象:返回认证成功的Authentication对象。

先看前半部分查看用户是否存在

在这里调用了retrieveUser方法来进行用户验证获取验证结果,这个方法在DaoAuthenticationProvider中进行验证,是否存在该用户。
在这里插入图片描述

DaoAuthenticationProvider中调用loadUserByUsername方法进行具体内容的验证,这个方法在前面UserDetailsService的作用中看到过
在这里插入图片描述

验证密码是否正确

在完成用户存在验证后,我们继续看AbstractUserDetailsAuthenticationProvider类,在这个类中使用additionalAuthenticationChecks方法进行账号密码的验证。
在这里插入图片描述

具体内容的实现仍在在DaoAuthenticationProvider
在这里插入图片描述

5)请求拦截

上面我们可以知道UsernamePasswordAuthenticationFilter拦截器,拦截的只是login的请求,那对于之后的每一次请求是个什么样的流程呢

通过拦截每一次请求,接着验证是否被授权,因此我们之后在处理请求拦截时,可以同样采用这样的方式,进行借鉴
在这里插入图片描述

6)汇总
发送登录请求
检查请求
默认
成功
失败
客户端
DispatcherServlet
Spring Security Filter Chain
SecurityContextHolder
UsernamePasswordAuthenticationFilter
请求路径和方法匹配?
提取用户名和密码
继续过滤器链
创建 UsernamePasswordAuthenticationToken
调用 AuthenticationManager
AuthenticationManager
委派给 AuthenticationProvider
DaoAuthenticationProvider
调用 UserDetailsService
UserDetailsService
加载 UserDetails
密码验证
返回 Authentication 对象
抛出 AuthenticationException
设置 SecurityContextHolder
认证成功处理
AuthenticationSuccessHandler
继续过滤器链
AuthenticationFailureHandler
重定向或返回成功响应
重定向到登录页面或显示错误
客户端
7)关于加密的过程

很多配置都是通过大致流程,因此可以扩展到理解其他的一些配置项。

我们发现在上面密码验证时,是设置了编码器,那我们从来没有配置过DaoAuthenticationProvider,这里的密码加密器是怎么配置的呢?

DaoAuthenticationProvider构造方法设置加密器的位置添加断点。然后执行程序时不断进入断点。
在这里插入图片描述
进入了InitializeUserDetailsBeanManagerConfigurer
在这里插入图片描述

5. 结合数据库进行用户认证

数据库和上面配置过程不同的是:

手动配置

  • 首先创建springSecurity的用户
  • 在登录时对用户进行认证
  • 与前面创建的用户进行匹配

数据库配置

  • 不需要创建用户
  • 登录时直接与数据库中的用户进行匹配

经过上面的过程,我们可以知道,主要的过程就是DaoAuthenticationProvider创建时设置的UserDetailsService,可以控制用户的认证。

1)引入数据库

我们采用springdatajpa操作数据库

向pom中添加

<dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><scope>runtime</scope>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

设置yml内容

spring:datasource:url: jdbc:mysql://localhost:3306/springsecurityusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driverjpa:hibernate:ddl-auto: update #自动生成数据库show-sql: true
1)创建实体类

创建好之后记得手动在数据库中添加一条数据用于测试

@Data
@Entity
@Table(name = "sys_user")
public class User {@Column(name = "user_id", unique = true, nullable = false, insertable = false, updatable = false)@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private int userId;@Column(name = "mobile")private String mobile;@Column(name = "pwd")private String password;@Column(name="identity")private int identity;@Column(name="nick_name")private String nickName;
}
2)创建Dao层
@Repository
public interface UserDao extends JpaRepository<User,Integer> {User findByMobileAndPassword(String mobile,String pwd);User findByMobile(String mobile);
}
3)仿照InMemoryUserDetailsManager创建MyUserDetailsManager

我们上面知道了,要想控制账号密码的验证,我们就需要自己注入UserDetailsService,这样他就不会采用系统本身的验证方案了。

@Component
public class MyUserDetailsManager implements UserDetailsService {@Resourceprivate UserDao userDao;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {User user = userDao.findByMobile(username);Collection<? extends GrantedAuthority> authorities = new ArrayList<>();return new org.springframework.security.core.userdetails.User(user.getMobile(),"{noop}"+user.getPassword(),// 这里{noop}前缀代表不进行加密,也就是匹配时与数据库中的明文相同即可true,true,true,true,authorities);}
}
4)进行登录测试

6.漏洞保护

6.1 csrf跨域保护请求禁用

如果不禁用csrf,那么所有的post请求均会被拒绝

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extendsWebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) {http.csrf(csrf -> csrf.disable());}
}

springsecurity实战应用

1. 构建项目

项目框架

在这里插入图片描述

配置文件

pom
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>javax.xml.bind</groupId><artifactId>jaxb-api</artifactId><version>2.3.1</version></dependency><!--jwt依赖--><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.0</version></dependency><!-- https://mvnrepository.com/artifact/cn.hutool/hutool-jwt --><dependency><groupId>cn.hutool</groupId><artifactId>hutool-jwt</artifactId><version>5.8.27</version></dependency><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-test</artifactId><scope>test</scope></dependency>
yml
server:port: 11012spring:datasource:url: jdbc:mysql://localhost:3306/springsecurityusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driverjpa:hibernate:ddl-auto: updateshow-sql: true

实体类

@Data
@Entity
@Table(name = "sys_user")
public class User {@Column(name = "user_id", unique = true, nullable = false, insertable = false, updatable = false)@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private int userId;@Column(name = "mobile")private String mobile;@Column(name = "pwd")private String password;@Column(name="identity")private int identity;@Column(name="nick_name")private String nickName;
}

在执行项目之后,会自动构建数据库,在构建好数据库之后

记得手动插入一条数据
在这里插入图片描述

dao层

@Repository
public interface UserDao extends JpaRepository<User,Integer> {User findByMobileAndPassword(String mobile,String pwd);User findByMobile(String mobile);
}

服务层

public interface UserService {String login(String username,String password);
}
@Service
public class UserServiceImpl implements UserService {UserDao userDao;public UserServiceImpl(UserDao userDao) {this.userDao = userDao;}@Overridepublic String login(String username, String password) {User user = userDao.findByMobileAndPassword(username, password);if (user!= null) {return "login success"+user.getNickName();} else {return "login fail";}}
}

控制层

@RestController
@RequestMapping("/user")
public class UserController {UserService userService;public UserController(UserService userService) {this.userService = userService;}@GetMapping("/test")public String test() {return "tt";}@GetMapping("/login")public String login(@RequestParam(name = "account") String account, @RequestParam(name = "password") String password) {return userService.login(account, password);}
}

springSecurity


WebSecurityConfig
@Configuration
public class WebSecurityConfig {//加密器@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}// 授权管理器@Beanpublic AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception {return authConfig.getAuthenticationManager();}@Bean@Order(1)public SecurityFilterChain apiFilterChain(HttpSecurity http) throws Exception {http.securityMatcher("/user/login").authorizeHttpRequests(authorize -> authorize.anyRequest().anonymous()  // 允许匿名访问 /user/login);return http.build();}//Spring Security过滤链@Beanpublic SecurityFilterChain otherFilterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests(authorize -> authorize.anyRequest().hasRole("ADMIN")).httpBasic(withDefaults());return http.build();}}

Order的大小用于指明在第几层,越小越靠上,可以理解为优先级,越小越大

如果不设置order,那么会按照先后顺序进行配置

  • 首先请求先通过order为1的过滤链,就是/user/login的请求,设置为允许匿名访问
  • 而对于没有设置过滤链的请求,就会使用第二个配置otherFilterChain。这个配置被认为在 apiFilterChain 之后,因为它的 @Order 值在 1 之后(没有 @Order 默认为最后)
DBUserDetailsManager

继承了UserDetailsService,当加载用户的时候,就会执行这里的loadUserByUsername

@Component
public class DBUserDetailsManager implements UserDetailsService {@Resourceprivate UserDao userDao;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {User user = userDao.findByMobile(username);if (user == null) {throw new UsernameNotFoundException(username);}return new MyUserDetail(user);}
}
MyUserDetail

新建自己的UserDetails,继承原来的UserDetails,在里面添加我们自己定义的用户类,这样可以方便的存储我们自己的用户信息

@Data
@NoArgsConstructor
@AllArgsConstructor
public class MyUserDetail implements UserDetails {private User user; // 这是自己定义的用户类@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {return null;}@Overridepublic String getPassword() {return user.getPassword();}@Overridepublic String getUsername() {return user.getMobile();}@Overridepublic boolean isAccountNonExpired() {return true;}@Overridepublic boolean isAccountNonLocked() {return true;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return true;}
}

2. 初步测试

在上面已经完成了接口的简单控制

我们可以通过访问localhost:11012/user/login?account=123&password=123

发现可以访问,并且登录成功

但是当我们访问localhost:11012/user/test

需要我们进行springsecurity的登录

BasicAuthenticationFilter

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

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

相关文章

别再手工缝合API了!开源LLMOps神器LMForge,让你像搭积木一样玩转AI智能体!

你是否受够了这些&#xff1f; 刚调通OpenAI的API&#xff0c;老板说“咱们试试国产模型降本增效”&#xff0c;你看着满屏的if-else只想说“我晕”。想给AI加上“查天气”、“执行代码”的能力&#xff0c;却发现Function Calling的代码复杂得让人头皮发麻。本地的Agentdemo惊…

window使用ffmep工具,加自定义脚本执行视频转码成h264(运营人员使用)

技术文章大纲&#xff1a;ffmep配合脚本使用1. 需要提供脚本给视频转码的给运营,给运营上传视频使用安装ffmep windows版本(目前我使用的就是windows)将脚本里面的执行路径修改成自己的电脑安装ffmep/bin/ffmep.exe路径处理好之后就点击执行2.环境准备ffmep windows版解压到一个…

Leetcode 240. 搜索二维矩阵 II 矩阵 / 二分

原题链接&#xff1a; Leetcode 240. 搜索二维矩阵 II 解法一&#xff1a;排除法 参考 【图解】排除法&#xff0c;一图秒懂&#xff01;&#xff08;Python/Java/C/C/Go/JS/Rust&#xff09; 从右上角&#xff1a; class Solution { public:bool searchMatrix(vector<vec…

OCR 证件识别:驱动澳门酒店自助入住智能化

澳门酒店作为国际旅游窗口&#xff0c;每日接待持多元证件的旅客&#xff0c;OCR 证件识别技术的应用&#xff0c;让自助入住终端实现 “一证通办”&#xff0c;大幅提升服务效率。​旅客在自助终端办理入住时&#xff0c;只需将护照、港澳通行证、回乡证、电子身份证等证件贴近…

深入解析汇编语言的奥秘

汇编语言简介汇编语言&#xff08;Assembly Language&#xff09;是一种低级编程语言&#xff0c;直接对应计算机的机器指令集。它通过助记符&#xff08;如 MOV、ADD&#xff09;代替二进制操作码&#xff0c;更接近硬件架构&#xff0c;常用于性能优化、嵌入式开发或逆向工程…

Nextcloud 实战:打造属于你的私有云与在线协作平台

随着数据安全与隐私保护意识的提升&#xff0c;越来越多的个人和组织选择自建云平台来替代公有云。Nextcloud 作为一款开源的文件同步与协作套件&#xff0c;不仅能实现类似网盘的文件存储与分享&#xff0c;还提供日历、联系人、即时通讯、在线文档编辑等协作功能&#xff0c;…

实践指南:利用衡石AI Data Agent实现自然语言驱动的指标开发与归因

在数字化转型的深水区&#xff0c;企业数据团队常面临两难困境&#xff1a;业务部门需要敏捷响应的指标分析&#xff0c;但传统BI工具依赖技术团队编写SQL&#xff0c;导致需求交付周期长达数周&#xff1b;而直接暴露底层数据又存在安全与合规风险。衡石科技推出的AI Data Age…

知微集:Python中的线程(三)

欢迎来到"一起学点什么吧"的合集「NLP知微集」。在这里&#xff0c;我们不愿宏大叙事&#xff0c;只聚焦于自然语言处理领域中那些细微却关键的“齿轮”与“螺丝钉”。我相信&#xff0c;真正深刻的理解&#xff0c;源于对细节的洞察。本期&#xff0c;我将为您拆解的…

动态规划入门:从记忆化搜索到动态规划

在开始对动态规划的讲解之前&#xff0c;我们需要先对记忆化搜索进行回顾&#xff1a; 什么是记忆化搜索&#xff1f; 在搜索过程中&#xff0c;当搜索树中存在大量重复的节点时&#xff0c;我们可以通过引入一个"备忘录"&#xff08;通常是一个数组或哈希表&#…

Boost搜索引擎 网络库与前端(4)

文章目录前言一、引入网络库模块引入cpp-httplibcpp-httplib测试正式编写http_server二、前端模块三、项目的可能拓展总结前言 终于到了最后一篇喽&#xff0c;嘻嘻&#xff01; 一、引入网络库模块 引入cpp-httplib 下载地址如下&#xff0c;我个人不喜欢新版本   cpp-http…

Flink反压问题

背景在使用flink的过程中&#xff0c;多次遇到过反压&#xff08;backpressure&#xff09;的问题&#xff0c;这通常是因为数据处理的速率超过了数据源或下游系统的处理能力导致。反压的底层剖析网络流控一个重要的概念是网络流控&#xff0c;如上图&#xff0c;不同的Consume…

Day5-中间件与请求处理

昨天搞定了异步优化&#xff0c;今天来解决一些实际问题。Day4的API虽然性能不错&#xff0c;但还缺少一些企业级应用必备的功能。 现在的问题 前端无法访问API&#xff08;跨域问题&#xff09;没有请求日志&#xff0c;出问题难以排查错误信息格式不统一缺少统一的请求处理机…

【LeetCode热题100道笔记】反转链表

题目描述 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 示例 1&#xff1a;输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[5,4,3,2,1] 示例 2&#xff1a;输入&#xff1a;head [1,2] 输出&#xff1a;[2,1] 示例 3&#xff1a;…

Oracle:select top 5

在Oracle数据库中实现SELECT TOP 5功能需采用特定语法&#xff0c;因其原生不支持TOP关键字。以下是两种主流实现方式&#xff1a;‌ROWNUM结合子查询‌先通过子查询排序数据&#xff0c;再在外层用ROWNUM限制行数&#xff1a;SELECT * FROM ( SELECT * FROM 表名 ORDER BY 排序…

Kubernetes(k8s) 增量更新 po

文章目录前言k8s 增量更新 po1. 导出要新建po 的控制器配置2. 配置详解3. 重新生效前言 如果您觉得有用的话&#xff0c;记得给博主点个赞&#xff0c;评论&#xff0c;收藏一键三连啊&#xff0c;写作不易啊^ _ ^。   而且听说点赞的人每天的运气都不会太差&#xff0c;实在…

基于stm32的车辆安全驾驶预警系统

若该文为原创文章&#xff0c;转载请注明原文出处。一、 项目背景与引言(一) 研究背景及意义道路交通安全是全球性的重大公共安全问题。据统计&#xff0c;绝大多数交通事故源于驾驶员的危险状态&#xff08;疲劳、分心、健康突发状况&#xff09;和危险驾驶行为&#xff08;超…

React学习教程,从入门到精通, React 新创建组件语法知识点及案例代码(11)

React 新创建组件语法知识点及案例代码 React 是由 Facebook 开发的一个用于构建用户界面的 JavaScript 库。随着 React 的不断发展&#xff0c;创建组件的方式也在不断演进。本文将详细介绍 React 中创建组件的最新语法&#xff0c;包括函数组件&#xff08;Functional Compo…

SQL Server全链路安全防护

SQL Server 的安全性是一个多层次、综合性的体系&#xff0c;旨在保护数据免受未授权访问、篡改和泄露。其核心安全机制可概括为以下几个方面&#xff1a;1. 身份验证&#xff08;Authentication&#xff09; Windows 身份验证&#xff1a; 使用 Windows 账户&#xff08;域/本…

如何利用Web3提升企业竞争力

在这个信息爆炸的时代&#xff0c;Web3技术以其独特的去中心化、透明性和用户主权特性&#xff0c;成为企业提升竞争力的新战场。本文将深入探讨企业如何把握Web3的浪潮&#xff0c;实现业务的飞跃。 1. 把握Web3的核心价值 Web3的核心在于去中心化、透明性和用户主权。这种模式…

HOW - 在浏览器下载一个 Excel 表格文件

文章目录一、技术方案二、前端具体实现代码分析转换逻辑注意事项一、技术方案 后台返回 base64 数据 {code: 0,data: "base64;...", }前端进行数据格式转化并下载成 Excel 文件 这篇文章主要介绍第二个步骤的实现。 二、前端具体实现 代码 src/utils/transform…