【SpringBoot3】Spring Security 核心概念

news2024/11/28 12:49:13

一、什么是 Spring Security

注:本文基于Spring Boot 3.2.1 以及 Spring Security 6.2.1

Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC、DI(依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。

此外,Spring Security是一个功能强大且高度可定制的身份验证和访问控制框架,致力于为Java应用程序提供身份验证和授权的能力。

其两大核心功能是用户认证用户授权。用户认证是验证某个用户是否为系统中的合法主体,即用户能否访问该系统,一般要求用户提供用户名和密码。用户授权则是验证某个用户是否有权限执行某个操作。

Spring Security的前身是Acegi Security,是Spring项目组中用来提供安全认证服务的框架。Spring Security为JavaEE企业级开发提供了全面的安全防护,采用“安全层”的概念,使每一层都尽可能安全,连续的安全层可以达到全面的防护。Spring Security可以在Controller层、Service层、DAO层等以加注解的方式来保护应用程序的安全。

请注意,一个系统的安全还需要考虑传输层和系统层的安全,例如采用Https协议、服务器部署防火墙等。此外,将工程重新部署到一个新的服务器上时,不需要为Spring Security做额外的工作。

二、Spring Security 架构

参考 Spring Security Architecture

1、Servlet 过滤器

Spring Security的Servlet支持基于Servlet过滤器,因此首先查看过滤器的作用通常会很有帮助。下图展示了单个HTTP请求的处理程序的典型分层。
在这里插入图片描述

客户端发送请求到应用程序,容器创建一个FilterChain,其中包含Filter实例和应该处理HttpServletRequest的Servlet,基于请求URI的路径。

在Spring MVC应用程序中,Servlet是DispatcherServlet的一个实例。最多只能有一个Servlet处理单个HttpServletRequestHttpServletResponse。然而,可以使用多个Filter来:

  1. 防止调用下游Filter实例或Servlet。在这种情况下,Filter通常会写入HttpServletResponse
  2. 修改下游Filter实例和Servlet使用的HttpServletRequestHttpServletResponse

Filter的强大之处在于传递给它的FilterChain。

2、DelegatingFilterProxy

Spring提供了一个名为DelegatingFilterProxy的Filter实现,允许在Servlet容器的生命周期和Spring的ApplicationContext之间建立桥接。Servlet容器允许使用自己的标准注册Filter实例,但它不知道Spring定义的Bean。你可以通过标准的Servlet容器机制注册DelegatingFilterProxy,但将所有工作委托给实现了Filter接口的Spring Bean。

以下是DelegatingFilterProxy如何适配到Filter实例和FilterChain的示意图。

在这里插入图片描述

3、FilterChainProxy

Spring Security的Servlet支持包含在FilterChainProxy中。FilterChainProxy是Spring Security提供的一个特殊Filter,通过SecurityFilterChain允许委派给多个Filter实例。由于FilterChainProxy是一个Bean,通常会被包装在DelegatingFilterProxy中。

以下图片展示了FilterChainProxy的作用。
在这里插入图片描述

4、SecurityFilterChain

SecurityFilterChainFilterChainProxy用于确定哪些Spring Security Filter实例应该被调用处理当前请求。

以下图片展示了SecurityFilterChain的作用。
在这里插入图片描述

SecurityFilterChain中的安全过滤器通常是Bean,但它们是通过FilterChainProxy注册的,而不是通过DelegatingFilterProxy。FilterChainProxy相比直接注册到Servlet容器或DelegatingFilterProxy具有许多优势。首先,它为所有Spring Security的Servlet支持提供了一个起点。因此,如果您尝试排查Spring Security的Servlet支持问题,在FilterChainProxy中添加一个调试点是一个很好的起点。

其次,由于FilterChainProxy对于Spring Security的使用至关重要,它可以执行一些被视为必要的任务。例如,它清除SecurityContext以避免内存泄漏。它还应用了Spring Security的HttpFirewall来保护应用程序免受某些类型的攻击。

此外,它在确定何时调用SecurityFilterChain时提供了更多灵活性。在Servlet容器中,Filter实例仅基于URL进行调用。但是,FilterChainProxy可以使用RequestMatcher接口根据HttpServletRequest中的任何内容确定调用。

以下图片显示了多个SecurityFilterChain实例:

在这里插入图片描述

5、Security Filters

Security Filters通过SecurityFilterChain API插入到FilterChainProxy中。这些过滤器可以用于许多不同的目的,如身份验证、授权、防范利用漏洞等。这些过滤器按特定顺序执行,以确保它们在正确的时间被调用,例如,执行身份验证的过滤器应该在执行授权的过滤器之前被调用。通常情况下,不需要知道Spring Security的过滤器的顺序。然而,有时候知道顺序是有益的,如果你想知道它们,可以查看FilterOrderRegistration代码。

为了举例说明上述段落,让我们考虑以下安全配置:

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .csrf(Customizer.withDefaults())
            .authorizeHttpRequests(authorize -> authorize
                .anyRequest().authenticated()
            )
            .httpBasic(Customizer.withDefaults())
            .formLogin(Customizer.withDefaults());
        return http.build();
    }

}

上述配置将导致以下过滤器排序:

FilterAdded by
CsrfFilterHttpSecurity#csrf
UsernamePasswordAuthenticationFilterHttpSecurity#formLogin
BasicAuthenticationFilterHttpSecurity#httpBasic
AuthorizationFilterHttpSecurity#authorizeHttpRequests

首先,CsrfFilter被调用以防止跨站请求伪造(CSRF)攻击。
其次,身份验证过滤器被调用以对请求进行身份验证。
最后,AuthorizationFilter被调用以授权请求。

6、增加自定义过滤器到 Filter Chain

大多数情况下,默认的安全过滤器足以为您的应用程序提供安全保障。然而,有时您可能想要将自定义过滤器添加到安全过滤器链中。

例如,假设您想要添加一个过滤器,该过滤器获取租户ID标头并检查当前用户是否有权限访问该租户。前面的描述已经为我们提供了在哪里添加过滤器的线索,因为我们需要知道当前用户,所以我们需要在身份验证过滤器之后添加它。

首先,让我们创建过滤器:

import java.io.IOException;

import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import org.springframework.security.access.AccessDeniedException;

public class TenantFilter implements Filter {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;

        String tenantId = request.getHeader("X-Tenant-Id"); (1)
        boolean hasAccess = isUserAllowed(tenantId); (2)
        if (hasAccess) {
            filterChain.doFilter(request, response); (3)
            return;
        }
        throw new AccessDeniedException("Access denied"); (4)
    }

}

不需要实现Filter接口,您可以扩展OncePerRequestFilter,它是一种基类过滤器,每个请求只调用一次,并提供了带有HttpServletRequestHttpServletResponse参数的doFilterInternal方法。

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.filter.OncePerRequestFilter;

public class CustomFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        // Your custom logic here
        // For example, you can access request headers, retrieve the tenant id header,
        // and check if the current user has access to that tenant
        
        // Proceed to the next filter in the chain
        filterChain.doFilter(request, response);
    }
}

现在,我们需要将过滤器添加到安全过滤器链中。

@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http
        // ...
        .addFilterBefore(new TenantFilter(), AuthorizationFilter.class);
    return http.build();
}

7、Security 异常处理

异常转换过滤器(ExceptionTranslationFilter)允许将AccessDeniedExceptionAuthenticationException转换为HTTP响应。

异常转换过滤器被插入到FilterChainProxy中作为安全过滤器之一。

以下图片展示了异常转换过滤器与其他组件的关系:

在这里插入图片描述

第一,异常转换过滤器调用FilterChain.doFilter(request, response)来调用应用程序的其余部分。

第二,如果用户未经过身份验证或者出现了AuthenticationException,则开始进行身份验证。

  • SecurityContextHolder被清除。
  • HttpServletRequest被保存,以便在身份验证成功后重新执行原始请求。
  • 使用AuthenticationEntryPoint请求客户端提供凭据。例如,它可能重定向到登录页面或发送WWW-Authenticate头部。

第三,否则,如果是AccessDeniedException,则拒绝访问。调用AccessDeniedHandler来处理访问被拒绝的情况。

三、身份认证

参考 Authentication

认证机制

  • 用户名和密码 - 如何使用用户名/密码进行身份验证
  • OAuth 2.0登录 - 使用OAuth 2.0登录OpenID Connect和非标准的OAuth 2.0登录(如GitHub)
  • SAML 2.0登录 - SAML 2.0登录
  • 中央认证服务器(CAS) - 中央认证服务器(CAS)支持
  • 记住我 - 如何在会话过期后记住用户
  • JAAS认证 - 使用JAAS进行身份验证
  • 预认证场景 - 使用外部机制(如SiteMinder或Java EE安全性)进行身份验证,但仍使用Spring Security进行授权和保护以防止常见的攻击。
  • X509身份验证 - X509身份验证

这些机制为开发者提供了多种身份验证选项,以满足不同应用的安全需求和用户体验要求。例如,对于大多数基于 Web 的应用程序,基本的用户名和密码验证是足够的。然而,对于需要第三方身份验证提供者支持的应用程序,OAuth 2.0 和 OpenID Connect 是理想的选择。SAML 2.0 通常用于企业环境中的单点登录(SSO)解决方案。

“记住我”功能通过在用户浏览器中存储持久化令牌来实现,使用户在关闭浏览器或会话过期后无需重新登录即可保持登录状态。这可以提高用户体验,但也可能增加安全风险,因此应谨慎实现。

JAAS(Java Authentication and Authorization Service)是 Java 平台提供的一个安全框架,它允许开发者灵活地实现身份验证和授权。预身份验证场景是指使用外部的身份验证机制(如 SiteMinder 或 Java EE 安全性),然后再利用 Spring Security 进行授权和防止常见的安全漏洞。

最后,X509 身份验证是一种基于公钥和私钥证书的身份验证机制,通常用于需要高级别安全保证的场景,如 SSL/TLS 通信。

总的来说,这些身份验证机制为 Spring Security 提供了强大的功能,使开发者能够根据应用的需求和安全要求选择最适合的身份验证方式。

Servlet 身份验证架构

参考 Servlet Authentication Architecture

这个讨论扩展了关于Servlet安全性的概述,以描述在Servlet身份验证中使用的Spring Security的主要架构组件。如果你需要具体的流程来解释这些组件如何协同工作,请查看关于身份验证机制的特定部分。

  • SecurityContextHolder - SecurityContextHolder是Spring Security存储已认证用户详细信息的地方。
  • SecurityContext - 从SecurityContextHolder获取,包含当前已认证用户的身份验证信息。
  • Authentication - 可以是提供给AuthenticationManager的输入,以提供用户为进行身份验证所提供的凭据,或者是从SecurityContext中获取的当前用户。
  • GrantedAuthority - 在身份验证上授予主体的权限(例如,角色、范围等)。
  • AuthenticationManager - 定义Spring Security的过滤器如何执行身份验证的API。
  • ProviderManager - AuthenticationManager的最常见实现。
  • AuthenticationProvider - 由ProviderManager使用来执行特定类型的身份验证。
  • 使用AuthenticationEntryPoint请求凭据 - 用于从客户端请求凭据(例如,重定向到登录页面,发送WWW-Authenticate响应等)。
  • AbstractAuthenticationProcessingFilter - 用于身份验证的基础过滤器。这也很好地说明了身份验证的高级流程和各个组件如何协同工作。

这些组件共同构成了Spring Security在Servlet身份验证中的核心架构。通过理解每个组件的作用和它们如何相互协作,开发者可以更好地配置和实现适合其应用程序需求的身份验证策略。

(1)SecurityContextHolder

Spring Security的认证模型的核心是SecurityContextHolder。SecurityContextHolder包含了SecurityContext。

在这里插入图片描述
默认情况下,SecurityContextHolder使用ThreadLocal来存储这些细节,这意味着对于同一个线程中的方法,SecurityContext总是可用的,即使SecurityContext没有被明确地作为参数传递给这些方法。如果你注意在处理当前主体的请求后清理线程,这样使用ThreadLocal是相当安全的。Spring Security的FilterChainProxy确保SecurityContext总是被清除。

由于某些应用程序与线程的工作方式特定,因此它们可能不完全适合使用ThreadLocal。例如,Swing客户端可能希望Java虚拟机中的所有线程都使用相同的安全上下文。你可以在启动时配置SecurityContextHolder的策略,以指定你希望如何存储上下文。对于独立应用程序,你会使用SecurityContextHolder.MODE_GLOBAL策略。其他应用程序可能希望由安全线程生成的新线程也采用相同的安全标识。你可以通过使用SecurityContextHolder.MODE_INHERITABLETHREADLOCAL来实现这一点。你可以通过两种方式从默认的SecurityContextHolder.MODE_THREADLOCAL更改模式。第一种是设置系统属性。第二种是调用SecurityContextHolder上的静态方法。大多数应用程序无需更改默认值。

可以通过如下图所示配置JVM参数-Dspring.security.strategy=MODE_GLOBAL 来配置SecurityContextHolder的策略
在这里插入图片描述

(2)SecurityContext

SecurityContext是从SecurityContextHolder中获取的。SecurityContext包含一个Authentication对象。

(3)Authentication

在Spring Security中,Authentication接口主要有两个用途:

  1. 作为AuthenticationManager的输入,提供用户为进行身份验证而提供的凭据。在这种情况下,isAuthenticated()返回false。

  2. 代表当前已验证的用户。你可以从SecurityContext中获取当前的Authentication。

Authentication包含以下信息:

  • principal:标识用户。当使用用户名/密码进行身份验证时,这通常是UserDetails的实例。

  • credentials:通常是密码。在许多情况下,用户在身份验证后此字段会被清除,以确保不会泄露密码。

  • authorities:GrantedAuthority实例表示用户被授予的高级权限。两个例子是角色和范围。

(4)GrantedAuthority

GrantedAuthority实例是用户被授予的高级权限。

你可以通过Authentication.getAuthorities()方法获取GrantedAuthority实例。这个方法提供了一组GrantedAuthority对象的集合。GrantedAuthority,顾名思义,是授予主体的权限。这些权限通常是“角色”,如ROLE_ADMINISTRATOR或ROLE_HR_SUPERVISOR。这些角色随后被配置用于Web授权、方法授权和域对象授权。Spring Security的其他部分会解释这些权限并期望它们存在。在使用基于用户名/密码的身份验证时,GrantedAuthority实例通常由UserDetailsService加载。

通常,GrantedAuthority对象是应用程序级别的权限。它们不特定于给定的域对象。因此,你不太可能有一个GrantedAuthority来表示对Employee对象编号54的权限,因为如果有数千个这样的权限,你会很快耗尽内存(或者至少会导致应用程序花费很长时间来验证用户)。当然,Spring Security是专门设计来处理这种常见需求的,但你应该使用项目的域对象安全功能来满足这一目的。

(5)AuthenticationManager

AuthenticationManager是定义Spring Security过滤器如何执行身份验证的API。控制器(即Spring Security过滤器实例)在调用AuthenticationManager后,会将返回的Authentication设置到SecurityContextHolder中。如果你不与Spring Security过滤器实例集成,你可以直接设置SecurityContextHolder,并不需要使用AuthenticationManager。

虽然AuthenticationManager的实现可以是任何形式,但最常见的实现是ProviderManager。

(6)ProviderManager

ProviderManager是AuthenticationManager最常用的实现。ProviderManager委托给一组AuthenticationProvider实例。每个AuthenticationProvider都有机会表示身份验证应该成功、失败,或者表示无法做出决定并让下游的AuthenticationProvider来决定。如果配置的所有AuthenticationProvider实例都无法进行身份验证,那么身份验证将失败并抛出ProviderNotFoundException异常,这是一个特殊的AuthenticationException异常,表示ProviderManager没有被配置为支持传入的Authentication类型。

(7)AbstractAuthenticationProcessingFilter

AbstractAuthenticationProcessingFilter 是一个基础过滤器,用于验证用户的凭证。在凭证可以被验证之前,Spring Security 通常使用 AuthenticationEntryPoint 来请求这些凭证。

接下来,AbstractAuthenticationProcessingFilter 可以验证提交给它的任何身份验证请求。

在这里插入图片描述

1)当用户提交他们的凭证时,AbstractAuthenticationProcessingFilter会从HttpServletRequest中创建一个Authentication对象以进行身份验证。创建的Authentication类型取决于AbstractAuthenticationProcessingFilter的子类。例如,UsernamePasswordAuthenticationFilter会从HttpServletRequest中提交的用户名和密码创建一个UsernamePasswordAuthenticationToken。

2)接下来,Authentication对象会被传递给AuthenticationManager进行身份验证。

3)如果身份验证失败,则进入失败处理流程。

  • SecurityContextHolder会被清空。

  • RememberMeServices.loginFail会被调用。如果未配置“记住我”功能,则此操作为空操作。请参见rememberme包。

  • AuthenticationFailureHandler会被调用。请参见AuthenticationFailureHandler接口。

4)如果身份验证成功,则进入成功处理流程。

  • SessionAuthenticationStrategy会收到新的登录通知。请参见SessionAuthenticationStrategy接口。

  • Authentication对象会被设置在SecurityContextHolder上。之后,如果你需要保存SecurityContext以便它可以在未来的请求中自动设置,必须显式调用SecurityContextRepository#saveContext。请参见SecurityContextHolderFilter类。

  • RememberMeServices.loginSuccess会被调用。如果未配置“记住我”功能,则此操作为空操作。请参见rememberme包。

  • ApplicationEventPublisher会发布一个InteractiveAuthenticationSuccessEvent事件。

  • AuthenticationSuccessHandler会被调用。请参见AuthenticationSuccessHandler接口。

在更详细的流程中:

  1. AuthenticationEntryPoint: 当一个未经过身份验证的请求到达Spring Security时,AuthenticationEntryPoint 会被调用。它通常负责引导用户到登录页面或返回特定的HTTP状态码(如401 Unauthorized),以便客户端知道需要提交身份验证凭证。

  2. 提交凭证: 一旦用户通过登录表单或其他方式提供了凭证(如用户名和密码),这些凭证会被发送到服务器。这通常是通过一个POST请求到特定的URL(如 /login)来完成的。

  3. AbstractAuthenticationProcessingFilter: 这个过滤器被配置为拦截包含身份验证凭证的请求,并处理它们。它会尝试从请求中提取凭证(如从表单字段中获取用户名和密码),然后使用这些凭证来创建一个 Authentication 对象。然后,它会将 Authentication 对象传递给一个或多个 AuthenticationProvider 以进行验证。

  4. AuthenticationProvider: AuthenticationProvider 负责验证 Authentication 对象中的凭证。它可能会查询数据库、LDAP服务器或其他数据存储来验证提供的用户名和密码是否匹配。如果凭证有效,AuthenticationProvider 将返回一个完全填充的、经过身份验证的 Authentication 对象。

  5. SecurityContextHolder: 一旦 Authentication 对象被验证,它就会被设置到 SecurityContextHolder 中,这是一个线程内对象,存储了关于当前用户身份验证的信息。这允许后续的过滤器或控制器能够访问这个信息,以便根据用户的身份来执行授权决策。

总结来说,AbstractAuthenticationProcessingFilter 在Spring Security中扮演着关键角色,它处理用户提交的凭证,并将它们传递给 AuthenticationProvider 进行验证。如果验证成功,用户的身份验证状态将被存储在 SecurityContextHolder 中,供后续使用。

参考

  • https://docs.spring.io/spring-security/reference/index.html

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

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

相关文章

Vue | (一)Vue核心(下) | 尚硅谷Vue2.0+Vue3.0全套教程

文章目录 📚class与style绑定📚条件渲染📚列表渲染🐇基本列表🐇key的原理🐇列表过滤(搜索)🐇列表排序🐇Vue数据监测 📚收集表单数据📚…

2024/2/18:IO进程线程

作业1&#xff1a;使用fgets统计给定文件的行数 代码&#xff1a; #include <stdio.h> #include <string.h> #include <stdlib.h> int main(int argc, const char *argv[]) {//定义FILE *类型的句柄FILE *fpNULL;//判断是否进行了外部传参if(argc ! 2){pri…

JRT监听-PDF-Excel-Img

依赖全新设计&#xff0c;我们无需再顾虑历史兼容性的束缚&#xff1b;同时&#xff0c;基于多年来累积的深入需求理解&#xff0c;JRT监听机制巧妙地借助CMD命令模式&#xff0c;达成了监听的全面统一。无论是PDF、Excel还是图片文件&#xff0c;都不再需要特殊对待或额外区分…

html的无语义标签:div span

html的无语义标签&#xff1a;div & span 无语义标签&#xff1a;div & span 标题&#xff0c;段落&#xff0c;图片等都是通过固定的标签来表示&#xff0c;标题用h1~h6标签来表示&#xff0c;段落用p标签来表示&#xff0c;图片用img标签来表示……每个标签都有自己…

电商数据分析数据统计数据监控必备-电商API电商数据接口

API&#xff0c;全称Application Programming Interface&#xff0c;是一种用于不同应用程序间通信的接口&#xff0c;它允许不同的应用程序之间交换数据和功能。API可以理解为应用程序提供给其他应用程序或开发者的接口&#xff0c;通过这个接口&#xff0c;其他应用程序或开发…

质量当先:国辰智企QMS产品质量追溯平台助力电子企业发展

在当今竞争激烈的电子产品市场中&#xff0c;质量是企业成功的关键。为了满足客户对高品质产品的需求&#xff0c;企业需要一套可靠的质量管理系统。QMS产品质量追溯平台为电子产品行业提供了全面的解决方案&#xff0c;确保产品质量的可追溯性和持续改进。 产品质量追溯平台是…

【软件设计师】程序猿需掌握的技能——数据流图

作为一个程序员&#xff0c;不仅要具备高水平的程序编码能力&#xff0c;还要是熟练掌握软件设计的方法和技术&#xff0c;具有一定的软件设计能力&#xff0c;一般包括软件分析设计图&#xff08;常见的有数据流图&#xff0c;程序流程图&#xff0c;系统流程图&#xff0c;E-…

Python与SQLAlchemy:轻松管理数据库

在这篇文章中&#xff0c;我们将学习如何使用Python和SQLAlchemy库来轻松管理数据库。SQLAlchemy是一个强大的ORM&#xff08;对象关系映射&#xff09;库&#xff0c;它允许您通过Python代码与关系型数据库进行交互&#xff0c;而无需编写SQL语句。 一、安装SQLAlchemy 首先…

Android studio 中英文转换

一、确认版本号 需要确认android studio软件版本&#xff0c;根据版本下载对应的中文汉化包&#xff0c;如果安装的汉化包版本不对应&#xff0c;可能会导致安装失败。 面板选择Help→About&#xff0c;在弹出框中查看当前版本号&#xff0c;我们只需要关心版本号前面的222即可…

智慧图书管理系统架构设计与实现

随着数字化时代的到来&#xff0c;智慧图书管理系统在图书馆和机构中扮演着重要的角色。一个优秀的图书管理系统不仅需要满足基本的借阅管理需求&#xff0c;还需要具备高效的性能、良好的扩展性和稳定的安全性。本文将讨论智慧图书管理系统的架构设计与实现&#xff0c;以满足…

在 Python 中,通过列表字典创建 DataFrame 时,若字典的 key 的顺序不一样以及部分字典缺失某些键,pandas 将如何处理?

&#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ pandas 是一个快速、强大、灵活且易于使用的开源数据分析和处理工具&#xff0c;它是建立在 Python 编程语言之上的。 pandas 官方文档地址&#xff1a;https://pandas.pydata.org/ 在 Python 中&…

第九篇:node静态文件服务(中间件)

&#x1f3ac; 江城开朗的豌豆&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 &#x1f4dd; 个人网站 :《 江城开朗的豌豆&#x1fadb; 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! &#x1f4d8; 引言&#xff1a; 当今互联网时代&am…

IDEA实现序列化时如何自动生成serialVersionUID

实现步骤&#xff1a;1.安装GenerateSerialVersionUID插件 2.点击idea左上角File -> Settings -> Editor -> Inspections -> 搜索 Serialization issues &#xff0c;找到 Serializable class without ‘serialVersionUID’ ->打上勾&#xff0c;再点击Apply-&…

《剑指 Offer》专项突破版 - 面试题 49、50 和 51 : 详解与二叉树中路径相关的面试题(C++ 实现)

目录 面试题 49 : 从根节点到叶节点的路径数字之和 面试题 50 : 向下的路径节点值之和 面试题 51 : 节点值之和最大的路径 面试题 49 : 从根节点到叶节点的路径数字之和 题目&#xff1a; 在一棵二叉树中所有节点都在 0~9 的范围之内&#xff0c;从根节点到叶节点的路径表…

5G车载路由器引领无人驾驶车联网应用

随着无人驾驶技术的不断发展&#xff0c;车联网正逐渐成为实现智能交通的重要组成部分。5G车载路由器将在车联网的应用中起到至关重要的作用&#xff0c;它能够满足无人驾驶应用的低时延、高速率和实时控制等需求&#xff0c;进一步推动无人驾驶车联网技术。 5G路由器具备低时延…

ACK One:构建混合云同城容灾系统

作者&#xff1a;蔡靖 对于当前业务运行在 IDC 内的 Kubernetes 集群中&#xff0c;希望通过云计算为云下业务提供同城灾备的高可用冗余能力&#xff0c;可利用阿里云分布式云容器平台 ACK One [ 1] 来提供统一得流量、应用和集群管理&#xff0c;实现业务流量的多集群路由和灾…

Eclipse - Switch Workspace

Eclipse - Switch Workspace References Switch Workspace References [1] Yongqiang Cheng, https://yongqiang.blog.csdn.net/

解决Ubuntu下网络适配器桥接模式下ping网址不通的情况

问题反应&#xff1a;ping不通网址 打开虚拟机中的设置&#xff0c;更改网络适配器为NAT模式 确定保存更改之后&#xff0c;退出输入如下命令。 命令1&#xff1a; sudo /etc/network/inferfaces 命令2&#xff1a; sudo /etc/init.d/network/ restart

Poller模块与Channel模块整合

目录 概要 tcp_cli.cc tcp_srv.cc server.hpp 测试结果 gdb调试 概要 本主要是将以下模块进行整合测试 Buffer缓冲区类实现(模块一)-CSDN博客 Socket套接字类实现(模块二)-CSDN博客 Channel事件管理类实现(模块三)-CSDN博客 Poller描述符监控类实现(模块四)-CSDN博客 经…

guitar pro五线谱转六线谱 guitar pro怎么去掉五线谱 简谱是什么 新手学吉他

在音乐领域&#xff0c;五线谱和六线谱是两种常见的乐谱表示方法。五线谱通常用于表示管弦乐、钢琴和其他键盘类乐器的音乐&#xff0c;而六线谱则常用于表示吉他等弦乐器的音乐。Guitar Pro软件作为一款专业的吉他编奏软件&#xff0c;六线谱的音乐更适合在软件中编奏。因此&a…