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

Spring Security—Servlet 应用架构

 

目录

一、Filter(过滤器)回顾

二、DelegatingFilterProxy

三、FilterChainProxy

四、SecurityFilterChain

五、Security Filter

六、打印出 Security Filter

七、添加自定义 Filter 到 Filter Chain

八、处理 Security 异常

九、保存认证之间的请求

十、RequestCache


本站(springdoc.cn)中的内容来源于 spring.io ,原始版权归属于 spring.io。由 springdoc.cn 进行翻译,整理。可供个人学习、研究,未经许可,不得进行任何转载、商用或与之相关的行为。 商标声明:Spring 是 Pivotal Software, Inc. 在美国以及其他国家的商标。

本节讨论了 Spring Security 在基于 Servlet 的应用程序中的高级架构。我们在参考资料中的 认证(Authentication)、授权(Authorization) 和 防止漏洞 部分建立了这种高层次的理解。

一、Filter(过滤器)回顾

Spring Security 对 Servlet 的支持是基于Servlet过滤器的,所以先看一下过滤器的一般作用是很有帮助的。下图显示了单个HTTP请求的处理程序的典型分层。

Figure 1. FilterChain

客户端向应用程序发送一个请求,容器创建一个 FilterChain,其中包含 Filter 实例和 Servlet,应该根据请求URI的路径来处理 HttpServletRequest。在Spring MVC应用程序中,Servlet是 DispatcherServlet 的一个实例。一个 Servlet 最多可以处理一个 HttpServletRequest 和 HttpServletResponse。然而,可以使用多个 Filter 来完成如下工作。

  • 防止下游的 Filter 实例或 Servlet 被调用。在这种情况下,Filter 通常会使用 HttpServletResponse 对客户端写入响应。
  • 修改下游的 Filter 实例和 Servlet 所使用的 HttpServletRequest 或 HttpServletResponse。

过滤器的力量来自于传入它的 FilterChain。

FilterChain Usage Example

  • Java
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {// do something before the rest of the applicationchain.doFilter(request, response); // invoke the rest of the application// do something after the rest of the application
}

由于一个 Filter 只影响下游的 Filter 实例和 Servlet,所以每个 Filter 的调用顺序是非常重要的。

二、DelegatingFilterProxy

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

下面是 DelegatingFilterProxy 如何融入 Filter 实例和 FilterChain 的图片。

Figure 2. DelegatingFilterProxy

DelegatingFilterProxy 从 ApplicationContext 查找 Bean Filter0,然后调用 Bean Filter0。下面的列表显示了 DelegatingFilterProxy 的伪代码。

DelegatingFilterProxy Pseudo Code

  • Java
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {Filter delegate = getFilterBean(someBeanName); delegate.doFilter(request, response); 
}

延迟地获取被注册为Spring Bean的 Filter。 对于 DelegatingFilterProxy 中的例子,delegate 是 Bean Filter0 的一个实例。

将工作委托给 Spring Bean。

DelegatingFilterProxy 的另一个好处是,它允许延迟查找 Filter Bean实例。这一点很重要,因为在容器启动之前,容器需要注册 Filter 实例。然而, Spring 通常使用 ContextLoaderListener 来加载 Spring Bean,这在需要注册 Filter 实例之后才会完成。

三、FilterChainProxy

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

下图显示了 FilterChainProxy 的作用。

Figure 3. FilterChainProxy

四、SecurityFilterChain

SecurityFilterChain 被 FilterChainProxy 用来确定当前请求应该调用哪些 Spring Security Filter 实例。

下图显示了 SecurityFilterChain 的作用。

Figure 4. SecurityFilterChain

SecurityFilterChain 中的 Security Filter 通常是Bean,但它们是用 FilterChainProxy 而不是 DelegatingFilterProxy 注册的。与直接向Servlet容器或 DelegatingFilterProxy 注册相比,FilterChainProxy 有很多优势。首先,它为 Spring Security 的所有 Servlet 支持提供了一个起点。由于这个原因,如果你试图对 Spring Security 的 Servlet 支持进行故障诊断,在 FilterChainProxy 中添加一个调试点是一个很好的开始。

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

此外,它在确定何时应该调用 SecurityFilterChain 方面提供了更大的灵活性。在Servlet容器中,Filter 实例仅基于URL被调用。 然而,FilterChainProxy 可以通过使用 RequestMatcher 接口,根据 HttpServletRequest 中的任何内容确定调用。

下图显示了多个 SecurityFilterChain 实例。

Figure 5. Multiple SecurityFilterChain

在 Multiple SecurityFilterChain 图中, FilterChainProxy 决定应该使用哪个 SecurityFilterChain。只有第一个匹配的 SecurityFilterChain 被调用。如果请求的URL是 /api/messages/,它首先与 /api/** 的 SecurityFilterChain0 模式匹配,所以只有 SecurityFilterChain0 被调用,尽管它也与 SecurityFilterChainn 匹配。如果请求的URL是 /messages/,它与 /api/** 的 SecurityFilterChain0 模式不匹配,所以 FilterChainProxy 继续尝试每个 SecurityFilterChain。假设没有其他 SecurityFilterChain 实例相匹配,则调用 SecurityFilterChainn。

请注意,SecurityFilterChain0 只配置了三个 security Filter 实例。然而,SecurityFilterChainn 却配置了四个 security Filter 实例。值得注意的是,每个 SecurityFilterChain 都可以是唯一的,并且可以单独配置。事实上,如果应用程序希望 Spring Security 忽略某些请求,那么一个 SecurityFilterChain 可能会有零个 security Filter 实例。

五、Security Filter

Security Filter 是通过 SecurityFilterChain API 插入 FilterChainProxy 中的。

这些 filter 可以用于许多不同的目的,如 认证、 授权、 漏洞保护 等等。filter 是按照特定的顺序执行的,以保证它们在正确的时间被调用,例如,执行认证的 Filter 应该在执行授权的 Filter 之前被调用。一般来说,没有必要知道 Spring Security 的 Filter 的顺序。但是,有些时候知道顺序是有好处的,如果你想知道它们,可以查看 FilterOrderRegistration 代码。

为了解释上面这段话,让我们考虑以下 security 配置:

  • Java
@Configuration
@EnableWebSecurity
public class SecurityConfig {@Beanpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {http.csrf(Customizer.withDefaults()).authorizeHttpRequests(authorize -> authorize.anyRequest().authenticated()).httpBasic(Customizer.withDefaults()).formLogin(Customizer.withDefaults());return http.build();}}

上述配置中的 Filter 顺序如下:

Filter

添加者

CsrfFilter

HttpSecurity#csrf

UsernamePasswordAuthenticationFilter

HttpSecurity#formLogin

BasicAuthenticationFilter

HttpSecurity#httpBasic

AuthorizationFilter

HttpSecurity#authorizeHttpRequests

  1. 首先,调用 CsrfFilter 来防止 CSRF 攻击。
  2. 其次,认证 filter 被调用以认证请求。
  3. 第三,调用 AuthorizationFilter 来授权该请求。

可能还有其他的 Filter 实例没有在上面列出。如果你想看到为某个特定请求调用的 filter 列表,你可以 打印出它们。

六、打印出 Security Filter

很多时候,看到为某一特定请求调用的 security Filter 的列表是很有用的。例如,你想确保 你添加的 filter 在 security filter 的列表中。

filter 列表在应用程序启动时以 INFO 级别打印,因此你可以在控制台输出中看到类似下面的内容:

2023-06-14T08:55:22.321-03:00  INFO 76975 --- [           main] o.s.s.web.DefaultSecurityFilterChain     : Will secure any request with [
org.springframework.security.web.session.DisableEncodeUrlFilter@404db674,
org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@50f097b5,
org.springframework.security.web.context.SecurityContextHolderFilter@6fc6deb7,
org.springframework.security.web.header.HeaderWriterFilter@6f76c2cc,
org.springframework.security.web.csrf.CsrfFilter@c29fe36,
org.springframework.security.web.authentication.logout.LogoutFilter@ef60710,
org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@7c2dfa2,
org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter@4397a639,
org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter@7add838c,
org.springframework.security.web.authentication.www.BasicAuthenticationFilter@5cc9d3d0,
org.springframework.security.web.savedrequest.RequestCacheAwareFilter@7da39774,
org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@32b0876c,
org.springframework.security.web.authentication.AnonymousAuthenticationFilter@3662bdff,
org.springframework.security.web.access.ExceptionTranslationFilter@77681ce4,
org.springframework.security.web.access.intercept.AuthorizationFilter@169268a7]

而这将使我们对 每个 filter chain 所配置的 security filter 有一个相当好的了解。

但这还不是全部,你还可以配置你的应用程序来打印每个请求的每个 filter 的调用情况。这对于查看你所添加的 filter 是否为某个特定的请求所调用或检查异常的来源是有帮助的。要做到这一点,你可以配置你的应用程序来 记录 security event。

七、添加自定义 Filter 到 Filter Chain

大多数情况下,默认的 security filter 足以为你的应用程序提供安全。然而,有时你可能想在 security filter chain 中添加一个自定义的 filter。

例如,假设你想添加一个 Filter,获得一个租户 id header 并检查当前用户是否有访问该租户的权限。前面的描述已经给了我们一个添加 filter 的线索,因为我们需要知道当前的用户,所以我们需要在认证 filter 之后添加它。

首先,创建一个 Filter:

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 {@Overridepublic 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"); boolean hasAccess = isUserAllowed(tenantId); if (hasAccess) {filterChain.doFilter(request, response); return;}throw new AccessDeniedException("Access denied"); }}

上面的示例代码做了以下工作:

从请求头中获取租户ID。

检查当前用户是否对租户ID有访问权。

如果该用户有访问权,那么就调用链中其他的 filter。

如果用户没有权限,则抛出一个 AccessDeniedException。

你可以从 OncePerRequestFilter 中继承,而不是实现 Filter,这是一个基类,用于每个请求只调用一次的 filter,并提供一个带有 HttpServletRequest 和 HttpServletResponse 参数的 doFilterInternal 方法。

现在,我们需要把这个 filter 添加到 security filter chain 中。

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

使用 HttpSecurity#addFilterBefore 在 AuthorizationFilter 之前添加 TenantFilter。

通过在 AuthorizationFilter 之前添加filter,我们确保 TenantFilter 在认证 filter 之后被调用。你也可以使用 HttpSecurity#addFilterAfter 将 filter 添加到某个特定的 filter 之后,或者使用 HttpSecurity#addFilterAt 将 filter 添加到 filter chain 中的某个位置。

就这样,现在 TenantFilter 将在过 filter chain 中被调用,并将检查当前用户是否对租户ID有访问权。

当你把你的 filter 声明为 Spring Bean 时要小心,可以用 @Component 注解它,也可以在配置中把它声明为 Bean,因为 Spring Boot 会自动 在嵌入式容器中注册它。这可能会导致 filter 被调用两次,一次由容器调用,一次由 Spring Security 调用,而且顺序不同。

如果你仍然想把你的 filter 声明为 Spring Bean,以利用依赖注入,避免重复调用,你可以通过声明 FilterRegistrationBean Bean 并将其 enabled 属性设置为 false 来告诉 Spring Boot 不要向容器注册它:

@Bean
public FilterRegistrationBean<TenantFilter> tenantFilterRegistration(TenantFilter filter) {FilterRegistrationBean<TenantFilter> registration = new FilterRegistrationBean<>(filter);registration.setEnabled(false);return registration;
}

八、处理 Security 异常

ExceptionTranslationFilter 允许将 AccessDeniedException 和 AuthenticationException 翻译成 HTTP 响应。

ExceptionTranslationFilter 作为 Security Filter 之一被插入到 FilterChainProxy 中。

下面的图片显示了 ExceptionTranslationFilter 与其他组件的关系。

  • 首先,ExceptionTranslationFilter 调用 FilterChain.doFilter(request, response) 来调用应用程序的其他部分。
  • 如果用户没有被认证,或者是一个 AuthenticationException,那么就 开始认证
    • SecurityContextHolder 被清理掉。
    • HttpServletRequest 被保存起来,这样一旦认证成功,它就可以用来重放原始请求。
    • AuthenticationEntryPoint 用于请求客户的凭证。例如,它可以重定向到一个登录页面或发送一个 WWW-Authenticate 头。
  • 否则,如果是 AccessDeniedException,那么就是 Access Denied。 AccessDeniedHandler 被调用来处理拒绝访问(access denied)。

如果应用程序没有抛出 AccessDeniedException 或 AuthenticationException,那么 ExceptionTranslationFilter 就不会做任何事情。

ExceptionTranslationFilter 的伪代码看起来是这样的。

ExceptionTranslationFilter pseudocode

try {filterChain.doFilter(request, response); 
} catch (AccessDeniedException | AuthenticationException ex) {if (!authenticated || ex instanceof AuthenticationException) {startAuthentication(); } else {accessDenied(); }
}

正如 Filter(过滤器)回顾 中所述,调用 FilterChain.doFilter(request, response) 等同于调用应用程序的其他部分。这意味着,如果应用程序的另一部分(FilterSecurityInterceptor 或 method security)抛出一个 AuthenticationException 或 AccessDeniedException,它将在这里被捕获和处理。

如果用户没有被认证,或者是一个 AuthenticationException,则 开始认证

否则,拒绝访问(Access Denied)

九、保存认证之间的请求

正如在 处理 Security 异常 中所说明的,当一个请求没有认证,并且是针对需要认证的资源时,有必要保存认证资源的请求,以便在认证成功后重新请求。在Spring Security中,这是通过使用 RequestCache 实现来保存 HttpServletRequest 的。

十、RequestCache

HttpServletRequest 被保存在 RequestCache。当用户成功认证后,RequestCache 被用来重放原始请求。RequestCacheAwareFilter 就是使用 RequestCache 来保存 HttpServletRequest 的。

默认情况下,使用一个 HttpSessionRequestCache。下面的代码演示了如何定制 RequestCache 的实现,如果名为 continue 的参数存在,它将用于检查 HttpSession 是否有保存的请求。

Example 1. RequestCache Only Checks for Saved Requests if continue Parameter Present

Java

@Bean
DefaultSecurityFilterChain springSecurity(HttpSecurity http) throws Exception {HttpSessionRequestCache requestCache = new HttpSessionRequestCache();requestCache.setMatchingRequestParameterName("continue");http// ....requestCache((cache) -> cache.requestCache(requestCache));return http.build();
}

Kotlin

@Bean
open fun springSecurity(http: HttpSecurity): SecurityFilterChain {val httpRequestCache = HttpSessionRequestCache()httpRequestCache.setMatchingRequestParameterName("continue")http {requestCache {requestCache = httpRequestCache}}return http.build()
}

XML

<http auto-config="true"><!-- ... --><request-cache ref="requestCache"/>
</http><b:bean id="requestCache" class="org.springframework.security.web.savedrequest.HttpSessionRequestCache"p:matchingRequestParameterName="continue"/>

=== 防止请求被保存

有很多原因,你可能想不在 session 中存储用户的未经认证的请求。你可能想把这种存储卸载到用户的浏览器上,或者把它存储在数据库中。或者你可能想关闭这个功能,因为你总是想把用户重定向到主页,而不是他们登录前试图访问的页面。

要做到这一点,你可以使用 NullRequestCache 实现.

Java

@Bean
SecurityFilterChain springSecurity(HttpSecurity http) throws Exception {RequestCache nullRequestCache = new NullRequestCache();http// ....requestCache((cache) -> cache.requestCache(nullRequestCache));return http.build();
}

Kotlin

@Bean
open fun springSecurity(http: HttpSecurity): SecurityFilterChain {val nullRequestCache = NullRequestCache()http {requestCache {requestCache = nullRequestCache}}return http.build()
}

XML

<http auto-config="true"><!-- ... --><request-cache ref="nullRequestCache"/>
</http><b:bean id="nullRequestCache" class="org.springframework.security.web.savedrequest.NullRequestCache"/>

==== RequestCacheAwareFilter

RequestCacheAwareFilter 使用 RequestCache 来保存 HttpServletRequest。

== 日志

Spring Security 在 DEBUG 和 TRACE 级别提供了对所有 security 相关事件的全面记录。这在调试你的应用程序时非常有用,因为出于安全考虑,Spring Security 不会在响应体中添加任何关于请求被拒绝的细节。如果你遇到 401 或 403 错误,你很可能会找到一条日志信息,帮助你了解发生了什么。

让我们考虑一个例子,一个用户试图向一个启用了 CSRF保护 的资源发出一个 POST 请求,但没有CSRF令牌。在没有日志的情况下,用户会看到一个 403 错误,没有解释为什么请求被拒绝。然而,如果你为 Spring Security 启用了日志,你会看到这样的日志信息:

2023-06-14T09:44:25.797-03:00 DEBUG 76975 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : Securing POST /hello
2023-06-14T09:44:25.797-03:00 TRACE 76975 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : Invoking DisableEncodeUrlFilter (1/15)
2023-06-14T09:44:25.798-03:00 TRACE 76975 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : Invoking WebAsyncManagerIntegrationFilter (2/15)
2023-06-14T09:44:25.800-03:00 TRACE 76975 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : Invoking SecurityContextHolderFilter (3/15)
2023-06-14T09:44:25.801-03:00 TRACE 76975 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : Invoking HeaderWriterFilter (4/15)
2023-06-14T09:44:25.802-03:00 TRACE 76975 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : Invoking CsrfFilter (5/15)
2023-06-14T09:44:25.814-03:00 DEBUG 76975 --- [nio-8080-exec-1] o.s.security.web.csrf.CsrfFilter         : Invalid CSRF token found for http://localhost:8080/hello
2023-06-14T09:44:25.814-03:00 DEBUG 76975 --- [nio-8080-exec-1] o.s.s.w.access.AccessDeniedHandlerImpl   : Responding with 403 status code
2023-06-14T09:44:25.814-03:00 TRACE 76975 --- [nio-8080-exec-1] o.s.s.w.header.writers.HstsHeaderWriter  : Not injecting HSTS header since it did not match request to [Is Secure]

很明显,CSRF令牌缺失,这就是请求被拒绝的原因。

要配置你的应用程序来记录所有的 security 事件,你可以向你的应用程序添加以下内容:

properties

logging.level.org.springframework.security=TRACE

logback.xml

<configuration><appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"><!-- ... --></appender><!-- ... --><logger name="org.springframework.security" level="trace" additivity="false"><appender-ref ref="Console" /></logger>
</configuration>

相关文章:

Spring Security—Servlet 应用架构

目录 一、Filter&#xff08;过滤器&#xff09;回顾 二、DelegatingFilterProxy 三、FilterChainProxy 四、SecurityFilterChain 五、Security Filter 六、打印出 Security Filter 七、添加自定义 Filter 到 Filter Chain 八、处理 Security 异常 九、保存认证之间的…...

排序优化:如何实现一个通用的、高性能的排序函数?

文章来源于极客时间前google工程师−王争专栏。 几乎所有的编程语言都会提供排序函数&#xff0c;比如java中的Collections.sort()。在平时的开发中&#xff0c;我们都是直接使用&#xff0c;这些排序函数是如何实现的&#xff1f;底层都利用了哪种排序算法呢&#xff1f; 问题…...

车载开发学习——CAN总线

CAN总线又称为汽车总线&#xff0c;全程为“控制器局域网&#xff08;Controller Area Network&#xff09;”&#xff0c;即区域网络控制器&#xff0c;它将区域内的单一控制单元以某种形式连接在一起&#xff0c;形成一个系统。在这个系统内&#xff0c;大家以一种大家都认可…...

2023年知名国产数据库厂家汇总

随着信创国产化的崛起&#xff0c;大家纷纷在寻找可替代的国产数据库厂家。这里小编就给大家汇总了一些国内知名数据库厂家&#xff0c;仅供参考哦&#xff01; 2023年知名国产数据库厂家汇总 1、人大金仓 2、瀚高 3、高斯 4、阿里云 5、华为云 6、浪潮 7、达梦 8、南大…...

【ARM Coresight SoC-400/SoC-600 专栏导读】

文章目录 1. ARM Coresight SoC-400/SoC-600 专栏导读目录1.1 Coresight 专题1.1.1 Performance Profiling1.1.2 ARM Coresight DS-5 系列 1. ARM Coresight SoC-400/SoC-600 专栏导读目录 本专栏全面介绍 ARM Coresight 系统 及SoC-400, SoC-600 中的各个组件。 1.1 Coresigh…...

在Go中创建自定义错误

引言 Go提供了两种在标准库中创建错误的方法&#xff0c;[errors.New和fmt.Errorf]&#xff0c;当与用户交流更复杂的错误信息时&#xff0c;或在调试时与未来的自己交流时&#xff0c;有时这两种机制不足以充分捕获和报告所发生的情况。为了传达更复杂的错误信息并实现更多的…...

Vue.js2+Cesium1.103.0 十三、通过经纬度查询 GeoServer 发布的 wms 服务下的 feature 对象的相关信息

Vue.js2Cesium1.103.0 十三、通过经纬度查询 GeoServer 发布的 wms 服务下的 feature 对象的相关信息 Demo <template><divid"cesium-container"style"width: 100%; height: 100%;"><div style"position: absolute;z-index: 999;bott…...

使用STM32怎么喂狗 (IWDG)

STM32F1 的独立看门狗&#xff08;以下简称 IWDG&#xff09;。 STM32F1内部自带了两个看门狗&#xff0c;一个是独立看门狗 IWDG&#xff0c;另一个是窗口看门狗 WWDG&#xff0c; 本章只介绍独立看门狗 IWDG&#xff0c;窗口看门狗 WWDG 会在后面章节介绍。 本章要实现的功能…...

GEE:计算和打印GEE程序的执行时间

作者:CSDN @ _养乐多_ 本文记录了计算和打印程序的执行时间的Google Earth Engine (GEE)代码,并举例说明。 大家在执行GEE代码的时候,有时候为了对比两个不同的脚本,不知道代码执行花费了多少时间。本文记录了打印代码执行时间的函数,并举了一个应用案例说明。可以知道…...

GDPU 数据结构 天码行空5

一、实验目的 1&#xff0e;掌握队列的顺序存储结构 2&#xff0e;掌握队列先进先出运算原则在解决实际问题中的应用 二、实验内容 仿照教材顺序循环队列的例子&#xff0c;设计一个只使用队头指针和计数器的顺序循环队列抽象数据类型。其中操作包括&#xff1a;初始化、入队…...

SQLAlchemy学习-12.查询之 order_by 按desc 降序排序

前言 sqlalchemy的query默认是按id升序进行排序的&#xff0c;当我们需要按某个字段降序排序&#xff0c;就需要用到 order_by。 order_by 排序 默认情况下 sqlalchemy 的 query 默认是按 id 升序进行排序的 res session.query(Project).all() print(res) # [<Project…...

如何轻松打造数字人克隆系统+直播系统?OEM教你快速部署数字人SaaS系统源码

数字人做为国内目前最热门的人工智能创业赛道&#xff0c;连BAT都在跑步入局&#xff0c;中小企业更是渴望不渴及。但随着我国数字人头部品牌企业温州专帮信息科技有限公司旗下灰豚AI数字人平台的开源。使得中小企业零门槛可以轻松打造灰豚AI数字人一模一样的平台。灰豚数字人A…...

药物滥用第四篇介绍

OXY&#xff1a; 羟考酮&#xff08;Oxycodone&#xff0c;OXY&#xff09;&#xff0c;分子式为C18H21NO4&#xff0c;是一种半合成的蒂巴因衍生物。羟考酮为半合成的纯阿片受体激动药&#xff0c;其作用机制与吗啡相似&#xff0c;主要通过激动中枢神经系统内的阿片受体而起镇…...

Apache Doris (四十三): Doris数据更新与删除 - Update数据更新

🏡 个人主页:IT贫道_大数据OLAP体系技术栈,Apache Doris,Clickhouse 技术-CSDN博客 🚩 私聊博主:加入大数据技术讨论群聊,获取更多大数据资料。 🔔 博主个人B栈地址:豹哥教你大数据的个人空间-豹哥教你大数据个人主页-哔哩哔哩视频 目录 1. Update数据更新原理...

面试算法29:排序的循环链表

问题 在一个循环链表中节点的值递增排序&#xff0c;请设计一个算法在该循环链表中插入节点&#xff0c;并保证插入节点之后的循环链表仍然是排序的。 分析 首先分析在排序的循环链表中插入节点的规律。当在图4.15&#xff08;a&#xff09;的链表中插入值为4的节点时&…...

python中不可变类型和可变类型

不可变类型&#xff1a;修改之后内存存储地址不会发生改变 可变类型&#xff1a;修改之后内存存储地址发生改变 set...

vue3封装Axios库的 API 请求并使用拦截器来处理请求和响应

目录 为什么添加封装该部分&#xff1f; 具体代码&#xff1a; 对代码的解释&#xff1a; 如何使用&#xff1f; 为什么添加封装该部分&#xff1f; 简化发送 HTTP 请求的流程提供统一的错误处理机制支持用户状态管理和鉴权具备良好的扩展性和灵活性提高开发效率并使得代码…...

RK3588开发笔记(二):基于方案商提供sdk搭建引入mpp和sdk的宿主机交叉编译Qt5.12.10环境

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/133915614 红胖子网络科技博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬…...

rust学习——函数返回值

概念 Rust 中的函数定义以 fn 开始&#xff0c;后跟着函数名和一对圆括号。大括号告诉编译器函数体在哪里开始和结束。 特殊的地方——函数返回值 错误的写法 正解1 去掉分号 fn main() {let x plus_one(5);println!("The value of x is: {}", x); }fn plus_…...

【Cadence】配置文件cdsinit和cdsenv的使用

文件功能 .cdsinit文件&#xff1a;主要负责一些加载项的设置&#xff0c;一些脚本工具及一些快捷键 .cdsenv文件&#xff1a;主要负责一些环境变量或者参数的设置 文件位置&#xff1a; &#xff08;参照以下文件使用&#xff09; Virtuoso配置文件“.cdsenv”文件介绍和使…...

R语言AI模型部署方案:精准离线运行详解

R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...

边缘计算医疗风险自查APP开发方案

核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...

CentOS下的分布式内存计算Spark环境部署

一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架&#xff0c;相比 MapReduce 具有以下核心优势&#xff1a; 内存计算&#xff1a;数据可常驻内存&#xff0c;迭代计算性能提升 10-100 倍&#xff08;文档段落&#xff1a;3-79…...

1.3 VSCode安装与环境配置

进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件&#xff0c;然后打开终端&#xff0c;进入下载文件夹&#xff0c;键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...

【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)

可以使用Sqliteviz这个网站免费编写sql语句&#xff0c;它能够让用户直接在浏览器内练习SQL的语法&#xff0c;不需要安装任何软件。 链接如下&#xff1a; sqliteviz 注意&#xff1a; 在转写SQL语法时&#xff0c;关键字之间有一个特定的顺序&#xff0c;这个顺序会影响到…...

2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面

代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口&#xff08;适配服务端返回 Token&#xff09; export const login async (code, avatar) > {const res await http…...

ServerTrust 并非唯一

NSURLAuthenticationMethodServerTrust 只是 authenticationMethod 的冰山一角 要理解 NSURLAuthenticationMethodServerTrust, 首先要明白它只是 authenticationMethod 的选项之一, 并非唯一 1 先厘清概念 点说明authenticationMethodURLAuthenticationChallenge.protectionS…...

Robots.txt 文件

什么是robots.txt&#xff1f; robots.txt 是一个位于网站根目录下的文本文件&#xff08;如&#xff1a;https://example.com/robots.txt&#xff09;&#xff0c;它用于指导网络爬虫&#xff08;如搜索引擎的蜘蛛程序&#xff09;如何抓取该网站的内容。这个文件遵循 Robots…...

土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等

&#x1f50d; 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术&#xff0c;可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势&#xff0c;还能有效评价重大生态工程…...

HashMap中的put方法执行流程(流程图)

1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中&#xff0c;其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下&#xff1a; 初始判断与哈希计算&#xff1a; 首先&#xff0c;putVal 方法会检查当前的 table&#xff08;也就…...