Spring Security—Servlet 应用架构

news2024/11/19 19:42:55

 

目录

一、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 application
    chain.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 {

    @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();
    }

}

上述配置中的 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 {

    @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"); 
        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>

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

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

相关文章

关于统信UOS不能使用“modprobe brd”创建内存盘的问题

前言 我自用的电脑内存都比较大&#xff0c;因此很早就养成了使用内存做临时盘的习惯 内存盘的好处很多&#xff0c;比如将系统临时文件夹、浏览器缓存文件等设置到内存盘&#xff0c;不仅可以提升速度&#xff0c;还可以减少对固态硬盘的写入&#xff0c;提升固态盘的使用寿…

金融机器学习方法:回归分析

回归分析是统计学中的一个重要分支&#xff0c;它用于建立一个或多个自变量和一个因变量之间的关联模型。在本博客中&#xff0c;我们将深入探讨线性回归和逻辑回归这两种常见的回归分析方法&#xff0c;并通过Python示例进行分析。 目录 1.线性回归1.1 模型介绍1.2 示例分析 …

使用STM32怎么喂狗 (IWDG)

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

吉利银河L6征战2023混合动力汽车极限挑战赛获双冠,同级“优等生”不负众望

9月22-9月27日&#xff0c;由中汽信科携手昆明检验中心联合发起的国内首个混动汽车专属赛事2023混合动力汽车极限挑战赛在云南圆满结束。比赛项目涉及纯电续航里程、亏电油耗、高速真实能耗、高原山地极限能耗等多项衡量混动车买点的关键指标。在为期六天的挑战中&#xff0c;这…

java SpringBoot+Vue3打造企业级一体化SaaS系统视频课程,开发ERP与CRM系统实用课程(免费领取)

java SpringBootVue3打造企业级一体化SaaS系统视频课程&#xff0c;开发ERP与CRM系统实用课程&#xff08;免费领取&#xff09; &#xff1a; 查看文末领取课程 第1章 课程导学 1-1 、导学&#xff08;课程简介、ERP与CRM融合成为大趋势&#xff09; 1-2 、课程学习方法&am…

跨境电商商城源码(多语言多商户进出口电商平台)

一、跨境电商商城系统源码包括以下几个部分 前端框架&#xff1a;uni-app,vue 后端框架&#xff1a;ThinkPHP5.wokerman 支付系统&#xff1a;PayPal、USDT等主流支付平台 语言包&#xff1a;跨境电商支持15种语言&#xff0c;后续会增加 前端&#xff1a;包含APP端、小程序端、…

半主动悬架系统开发与测试(基于Modelbase实现)

ModelBase是经纬恒润开发的车辆仿真软件&#xff0c;包含两个大版本&#xff1a;动力学版本、智能驾驶版本。动力学版包含高精度动力学模型&#xff0c;能很好地复现车辆在实际道路中运行的各种状态变化&#xff0c;可用于乘用车、商用车动力底盘系统算法开发、控制器仿真测试&…

竞赛 深度学习乳腺癌分类

文章目录 1 前言2 前言3 数据集3.1 良性样本3.2 病变样本 4 开发环境5 代码实现5.1 实现流程5.2 部分代码实现5.2.1 导入库5.2.2 图像加载5.2.3 标记5.2.4 分组5.2.5 构建模型训练 6 分析指标6.1 精度&#xff0c;召回率和F1度量6.2 混淆矩阵 7 结果和结论8 最后 1 前言 &…

10_18Qt

头文件 #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include<QMovie> #include<QDebug> QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACEclass Widget : public QWidget {Q_OBJECTpublic:Widget(QWidget *parent nullptr)…

35 机器学习(三):混淆矩阵|朴素贝叶斯|决策树|随机森林

文章目录 分类模型的评估混淆矩阵精确率和召回率 接口介绍其他的补充 朴素贝叶斯基础原理介绍拉普拉斯平滑下面给出应用的例子朴素贝叶斯的思辨 决策树基础使用基本原理信息熵信息增益信息增益率Gini指数 剪枝api介绍 随机森林------集成学习初识基本使用api介绍 分类模型的评估…

基础设施SIG月度动态:T-One 社区版调度引擎全量替换至 runnerV2 版本,调度性能平均提升 6.8 倍

基础设施 SIG&#xff08;OpenAnolis Infra SIG&#xff09;目标&#xff1a;负责 OpenAnolis 社区基础设施工程平台的建设&#xff0c;包括官网、Bugzilla、Maillist、ABS、ANAS、CI 门禁以及社区 DevOps 相关的研发工程系统。 01 SIG 整体进展 1.官网 SIG 外链跳转增加确认…

List执行remove操作间歇性报错UnsupportedOperationException

废话不多说&#xff0c;直接上一个代码&#xff0c;意思很简单&#xff0c;就是把list中的"全部"置顶&#xff0c;但是不知道怎么会偶发报错。 List<UserDept> voList new ArrayList<>(userGroupService.findByName(groupName));for(UserDept userDept…

点云相关内容总结

点云相关内容总结 地形相关内容1. 机载点云地面点和非地面点识别 地形相关内容 1. 机载点云地面点和非地面点识别 识别场景中的梯度较大的区域&#xff0c;进行渐进三角网滤波&#xff0c;梯度大的地方补一下初始地面点 与其他软件相关算法结果进行对比 软件1 三角网滤波算…

塑料透光率测试可测试塑料部件的透明度和纯度

随着电子设备的快速发展&#xff0c;尤其是智能手机、平板电脑、可穿戴设备等新兴产品的普及&#xff0c;对塑料材料的需求量也在逐渐增加。因为这些电子设备需要大量的塑料材料来制造外壳、内部结构、部件等。电子设备在塑料行业的发展迅速&#xff0c;推动了塑料材料的技术进…

什么是低代码开发?低代码开发平台哪个更好?

什么是低代码开发&#xff1f; 有人觉得低代码只是个概念&#xff0c;一无是处&#xff1b; 有人将低代码吹上了天&#xff0c;将其送上神坛&#xff1b; 那么我们先来看看低代码开发是什么。 低代码开发&#xff08;Low-Code&#xff09;顾名思义&#xff0c;是以少量的代码…

铁威马新品F2-212上线,全新设计,极致使用体验

铁威马&#xff0c;作为国民专业级NAS&#xff0c;在过去的十多年里始终专注于存储技术的开发升级&#xff0c;旨在为用户提供全面、可靠且值得信赖的数据存储产品和解决方案。铁威马现在的产品线涵盖了2、4、5、6、8、9、12、16盘位等多种选择&#xff0c;不仅配置高端&#x…

海外问卷调查加盟可靠吗?

海外问卷调查加盟是可靠的&#xff0c;因为这本身就是一个稳定、长期的网络项目&#xff0c;已经存在十几年的时间了&#xff0c;一直都有人在靠它吃饭。 大家好&#xff0c;我是橙河老师&#xff0c;今天讲一讲海外问卷调查加盟可靠吗&#xff1f; 有许多朋友都曾尝试做过一…

Unity插件-Cinemachine

1.Virtual Camera 相机控制 创建Virtual Camera&#xff1a;鼠标右键&#xff08;或点击上方的GameObject&#xff09;-> Cinemachine -> Virtual Camera&#xff0c;创建完Virtual Camera后会发现场景原相机上会自动添加一个CinemachineBrain的组件 CinemachineBrain是…

程序员各阶段应该掌握的技术与能力

人人都是产品经理 | 产品经理、产品爱好者学习交流平台 (woshipm.com)

web前端基础CSS------美化页面“footer”部分

一&#xff0c;实验代码 <!DOCTYPE html> <html><head><meta charset"utf-8"><title>关于我们</title><style type"text/css">#footer{margin: 10px 0px;background: #f5f5f5;border: top 1px solid #eee ;}#f…