Authentication Persistence and Session Management

翻译版本 【spring-security 6.2.1 】session-managemen

Authentication Persistence and Session Management

一旦您获得了一个正在对请求进行身份验证的应用程序,就必须考虑如何在未来的请求中持久化和恢复所产生的身份验证。
默认情况下,这是自动完成的,所以不需要额外的代码,但重要的是要知道requireExplicitSave在HttpSecurity中的含义。

如果你喜欢,你可以关于requireExplicitSave是做什么的,或者为什么它很重要。否则,在大多数情况下,您已经完成了这一部分。

但在你离开之前,考虑一下这些用例是否适合你的应用:

  • 我想了解会话管理的组件
  • 我想限制用户可以同时登录的次数
  • 我想自己直接存储身份验证,而不是Spring Security为我做这件事
  • 我手动存储身份验证,我想删除它
  • 我正在使用SessionManagementFilter,我需要有关如何摆脱它的指导
  • 我希望将身份验证存储在会话之外的其他地方
  • 我正在使用无状态身份验证,但我仍然希望将其存储在会话中
  • 我正在使用SessionCreationPolicy.NEVER,但应用程序仍在创建会话。

Understanding Session Management’s Components

(了解会话管理的组件)

会话管理支持由几个组件组成,这些组件协同工作以提供功能。这些组件是SecurityContextHolderFilter、SecurityContextPersistenceFilter和SessionManagementFilter。

注意:
在Spring Security 6中,SecurityContextPersistenceFilter和SessionManagementFilter是默认不设置的。除此之外,任何应用程序都应该只设置SecurityContextHolderFilter或SecurityContextPersistenceFilter中的一个,而不是同时设置。

The SessionManagementFilter

SessionManagementFilter根据SecurityContextHolder的当前内容检查SecurityContextRepository的内容,以确定用户在当前请求期间是否已通过身份验证,通常通过非交互式身份验证机制,如pre-authentication或 remember-me。如果存储库包含安全上下文,则filter不执行任何操作。如果没有,并且线程本地SecurityContext包含一个((non-anonymous)Authentication对象,则filter假设它们已经由堆栈中的前一个filter进行了身份验证。然后,它将调用已配置的SessionAuthenticationStrategy。

如果用户当前未通过身份验证,则筛选器将检查是否请求了无效的会话ID(例如,由于超时),并将调用配置的InvalidSessionStrategy(如果已设置)。最常见的行为只是重定向到一个固定的URL,这被封装在标准实现SimpleRedirectInvalidSessionStrategy中。后者也用于通过命名空间配置无效会话URL,如前所述。

Moving Away From SessionManagementFilter

(远离SessionManagementFilter)

在Spring Security 5中,默认配置依赖于SessionManagementFilter来检测用户是否刚刚通过身份验证并调用SessionAuthenticationStrategy。问题在于,这意味着在典型的设置中,必须为每个请求读取HttpSession。

在SpringSecurity6中,默认情况是身份验证机制本身必须调用SessionAuthenticationStrategy。这意味着不需要检测身份验证何时完成,因此不需要为每个请求读取HttpSession。

Things To Consider When Moving Away From SessionManagementFilter

(远离SessionManagementFilter时要考虑的事情)

在Spring Security 6中,默认情况下不使用SessionManagementFilter,因此,来自sessionManagement DSL的一些方法将不会产生任何影响。

Method替换
sessionAuthenticationErrorUrl在身份验证机制中配置AuthenticationFailureHandler
sessionAuthenticationFailureHandler在身份验证机制中配置AuthenticationFailureHandler
sessionAuthenticationStrategy如上所述,在身份验证机制中配置SessionAuthenticationStrategy

如果您尝试使用这些方法中的任何一个,都会引发异常。

Customizing Where the Authentication Is Stored

(自定义认证存储位置)

默认情况下,Spring Security在HTTP会话中为您存储安全上下文。然而,这里有几个你可能想要定制的原因:

  • 您可能需要在HttpSessionSecurityContextRepository实例上调用单独的setter
  • 您可能希望将安全上下文存储在缓存或数据库中,以启用水平扩展

首先,您需要创建SecurityContextRepository的实现或使用现有的实现,如HttpSessionSecurityContextRepository,然后您可以在HttpSecurity中设置它。

自定义SecurityContextRepository

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) {SecurityContextRepository repo = new MyCustomSecurityContextRepository();http// ....securityContext((context) -> context.securityContextRepository(repo));return http.build();
}

注意:
上面的配置将在SecurityContextHolderFilter和参与的身份验证filters(如UsernamePasswordAuthenticationFilter)上设置SecurityContextRepository。要在无状态筛选器中设置它,请参阅如何为无状态身份验证自定义SecurityContextRepository。

Storing the Authentication manually

(手动保存身份验证)

例如,在某些情况下,您可能手动验证用户,而不是依赖于Spring Security过滤器。您可以使用自定义过滤器或SpringMVC控制器端点来完成此操作。如果你想保存请求之间的身份验证,例如在HttpSession中,你必须这样做:

private SecurityContextRepository securityContextRepository =new HttpSessionSecurityContextRepository();// 1@PostMapping("/login")
public void login(@RequestBody LoginRequest loginRequest, HttpServletRequest request, HttpServletResponse response) {// 2UsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.unauthenticated(loginRequest.getUsername(), loginRequest.getPassword());// 3Authentication authentication = authenticationManager.authenticate(token);// 4SecurityContext context = securityContextHolderStrategy.createEmptyContext();context.setAuthentication(authentication);// 5securityContextHolderStrategy.setContext(context);securityContextRepository.saveContext(context, request, response);// 6
}class LoginRequest {private String username;private String password;// getters and setters
}
  1. 将SecurityContextRepository添加到控制器
  2. 注入HttpServletRequest和HttpServletResponse来保存SecurityContext
  3. 使用提供的凭据创建未经身份验证的UsernamePasswordAuthenticationToken
  4. 调用AuthenticationManager#authenticate对用户进行身份验证
  5. 创建SecurityContext并在其中设置Authentication
  6. 将SecurityContext保存在SecurityContextRepository中

就是这样。如果你不确定上面例子中的securityContextHolderStrategy是什么,你可以在使用SecurityContextStrategy一节中关于它的信息。

Properly Clearing an Authentication

(正确清除身份验证)

如果您使用的是Spring Security的注销支持,那么它会为您处理很多事情,包括清除和保存上下文。但是,假设您需要手动将用户从应用程序中注销。在这种情况下,您需要确保正确地清除和保存上下文。

Configuring Persistence for Stateless Authentication

(配置无状态认证的持久性)

有时不需要创建和维护HttpSession,例如,在请求之间持久化身份验证。有些身份验证机制(如HTTP Basic)是无状态的,因此在每次请求时都要重新对用户进行身份验证。

如果您不希望创建会话,您可以使用SessionCreationPolicy.STATELESS,像这样:

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) {http// ....sessionManagement((session) -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS));return http.build();
}

上面的配置将SecurityContextRepository配置为使用NullSecurityContextRepository,并且还阻止将请求保存在会话中。

如果您正在使用SessionCreationPolicy.NEVER,您可能会注意到应用程序仍在创建HttpSession。在大多数情况下,发生这种情况是因为请求保存在会话中,以便在身份验证成功后重新请求经过身份验证的资源。要避免这种情况,请参考如何防止被保存请求一节。

Storing Stateless Authentication in the Session

(在会话中存储无状态身份验证)

如果出于某种原因,您正在使用无状态身份验证机制,但仍希望将身份验证存储在会话中,则可以使用HttpSessionSecurityContextRepository而不是NullSecurityContextRepository。

对于HTTP Basic,你可以添加一个ObjectPostProcessor来改变BasicAuthenticationFilter使用的SecurityContextRepository:

在HttpSession中存储HTTP Base 身份验证

@Bean
SecurityFilterChain web(HttpSecurity http) throws Exception {http// ....httpBasic((basic) -> basic.addObjectPostProcessor(new ObjectPostProcessor<BasicAuthenticationFilter>() {@Overridepublic <O extends BasicAuthenticationFilter> O postProcess(O filter) {filter.setSecurityContextRepository(new HttpSessionSecurityContextRepository());return filter;}}));return http.build();
}

上述内容也适用于其他认证机制,如Bearer Token Authentication。

Understanding Require Explicit Save

(了解需要显式保存)
在SpringSecurity5中,默认行为是使用SecurityContextPersistenceFilter将SecurityContext自动保存到SecurityContextRepository。保存必须在提交HttpServletResponse 之前和SecurityContextPersistenceFilter之前完成。不幸的是,当SecurityContext的自动持久化在请求完成之前(即,在提交HttpServletResponse 之前)完成时,可能会让用户感到惊讶。跟踪状态以确定是否需要保存也很复杂,有时会导致对SecurityContextRepository(即HttpSession)进行不必要的写入。

出于这些原因,SecurityContextPersistenceFilter已被弃用,取而代之的是SecurityContextHolderFilter。在Spring Security 6中,默认行为是SecurityContextHolderFilter将仅从SecurityContextRepository读取SecurityContext并将其填充到SecurityContextHolder中。如果用户希望SecurityContext在请求之间保持不变,那么他们现在必须显式地将SecurityContext与SecurityContextRepository一起保存。这消除了歧义,并通过只在必要时要求写入SecurityContextRepository(即HttpSession)来提高性能。

How it works

(它是如何工作的)
总而言之,当requireExplicitSave为true时,Spring Security会设置SecurityContextHolderFilter而不是SecurityContextPersistenceFilter。

Configuring Concurrent Session Control

(配置并发会话控制)

如果您希望对单个用户登录应用程序的能力施加限制,Spring Security通过以下简单的添加支持开箱即用。首先,你需要在你的配置中添加以下侦听器,以保持Spring Security对会话生命周期事件的更新:

@Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {return new HttpSessionEventPublisher();
}

然后将以下行添加到安全配置中:

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) {http.sessionManagement(session -> session.maximumSessions(1));return http.build();
}

这将防止用户多次登录—第二次登录将导致第一次登录无效。
使用Spring Boot,您可以通过以下方式测试上述配置场景:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
public class MaximumSessionsTests {@Autowiredprivate MockMvc mvc;@Testvoid loginOnSecondLoginThenFirstSessionTerminated() throws Exception {MvcResult mvcResult = this.mvc.perform(formLogin()).andExpect(authenticated()).andReturn();MockHttpSession firstLoginSession = (MockHttpSession) mvcResult.getRequest().getSession();this.mvc.perform(get("/").session(firstLoginSession)).andExpect(authenticated());this.mvc.perform(formLogin()).andExpect(authenticated());// first session is terminated by second loginthis.mvc.perform(get("/").session(firstLoginSession)).andExpect(unauthenticated());}}

您可以使用Maximum Sessions示例进行尝试。

同样常见的情况是,您希望阻止第二次登录,在这种情况下,您可以使用:

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) {http.sessionManagement(session -> session.maximumSessions(1).maxSessionsPreventsLogin(true));return http.build();
}

第二次登录将被拒绝。通过“拒绝”,我们的意思是如果使用基于表单的登录,用户将被发送到authentication-failure-url。如果通过另一种非交互机制(如“"remember-me”)进行第二次身份验证,则将向客户端发送“unauthorized”(401)错误。如果希望使用错误页面,则可以向会话管理元素添加属性session-authentication-error-url。

使用Spring Boot,您可以通过以下方式测试上述配置:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
public class MaximumSessionsPreventLoginTests {@Autowiredprivate MockMvc mvc;@Testvoid loginOnSecondLoginThenPreventLogin() throws Exception {MvcResult mvcResult = this.mvc.perform(formLogin()).andExpect(authenticated()).andReturn();MockHttpSession firstLoginSession = (MockHttpSession) mvcResult.getRequest().getSession();this.mvc.perform(get("/").session(firstLoginSession)).andExpect(authenticated());// second login is preventedthis.mvc.perform(formLogin()).andExpect(unauthenticated());// first session is still validthis.mvc.perform(get("/").session(firstLoginSession)).andExpect(authenticated());}}

如果您使用自定义的身份验证过滤器进行基于表单的登录,则必须显式配置并发会话控制支持。您可以使用“最大会话阻止登录”示例进行尝试。

Detecting Timeouts

(检测超时)
会话会自行过期,无需采取任何措施来确保删除安全上下文。也就是说,Spring Security可以检测会话何时过期,并采取您指示的特定操作。例如,当用户使用已过期的会话发出请求时,您可能希望重定向到特定的端点。这是通过HttpSecurity中的invalidSessionUrl实现的:

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) {http.sessionManagement(session -> session.invalidSessionUrl("/invalidSession"));return http.build();
}

请注意,如果使用此机制检测会话超时,如果用户退出登录,然后在不关闭浏览器的情况下重新登录,则可能错误地报告错误。这是因为会话cookie在会话无效时不会被清除,即使用户已经注销,也会被重新提交。如果是这种情况,您可能需要配置注销以清除会话cookie。

Customizing the Invalid Session Strategy

(自定义无效会话策略)

invalidSessionUrl是使用SimpleRedirectInvalidSessionStrategy实现设置InvalidSessionStrategy的方便方法。如果你想自定义行为,你可以实现InvalidSessionStrategy接口,并使用InvalidSessionStrategy方法配置它:

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) {http.sessionManagement(session -> session.invalidSessionStrategy(new MyCustomInvalidSessionStrategy()));return http.build();
}

Clearing Session Cookies on Logout

(注销时清除会话Cookie)

你可以在注销时显式地删除JSESSIONID cookie,例如在注销处理程序中使用Clear-Site-Data头:

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) {http.logout((logout) -> logout.addLogoutHandler(new HeaderWriterLogoutHandler(new ClearSiteDataHeaderWriter(COOKIES))));return http.build();
}

这样做的优点是与容器无关,并且可以与任何支持Clear-Site-Data头的容器一起工作。

作为替代方案,您还可以在注销处理程序中使用以下语法:

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) {http.logout(logout -> logout.deleteCookies("JSESSIONID"));return http.build();
}

不幸的是,不能保证这对每个servlet容器都有效,因此需要在您的环境中进行测试。

注意:
如果您在代理服务器后面运行应用程序,您还可以通过配置代理服务器来删除会话cookie。例如,通过使用Apache HTTPD的mod_headers,以下指令通过在响应注销请求时使JSESSIONID cookie过期来删除该cookie(假设应用程序部署在/tutorial路径下):

<LocationMatch "/tutorial/logout">
Header always set Set-Cookie "JSESSIONID=;Path=/tutorial;Expires=Thu, 01 Jan 1970 00:00:00 GMT"
</LocationMatch>

有关“清除站点数据”和“注销”部分的更多详细信息。

Understanding Session Fixation Attack Protection

(了解会话固定攻击保护)

会话固定攻击是一种潜在的风险,恶意攻击者可能通过访问网站创建会话,然后说服另一个用户使用同一会话登录(例如,通过向他们发送包含会话标识符作为参数的链接)。Spring Security通过创建新会话或在用户登录时更改会话ID来自动防止这种情况发生。

Configuring Session Fixation Protection

(配置会话固定保护)

您可以通过选择以下三个推荐选项来控制会话固定保护策略:

  • changeSessionId -不创建新会话。相反,使用Servlet容器提供的会话固定保护(HttpServletRequest#changeSessionId())。此选项仅在Servlet 3.1 (Java EE 7)和更新的容器中可用。在旧的容器中指定它将导致异常。这是Servlet 3.1和更新的容器中的默认值。
  • newSession -创建一个新的“干净”会话,不复制现有会话数据(Spring security相关属性仍将被复制)。
  • migrateSession -创建一个新会话,并将所有现有会话属性复制到新会话。这是Servlet 3.0或更早的容器中的默认值。

您可以通过执行以下操作来配置会话固定保护:

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) {http.sessionManagement((session) - session.sessionFixation((sessionFixation) -> sessionFixation.newSession()));return http.build();
}

发生会话固定保护时,会导致在应用程序上下文中发布SessionFixationProtectionEvent。如果您使用changeSessionId,这种保护也会导致任何jakarta.servlet.http.HttpSessionIdListeners收到通知,因此,如果您的代码同时侦听这两个事件,请小心。

您也可以将会话固定保护设置为none来禁用它,但不建议这样做,因为这会使您的应用程序容易受到攻击。

Using SecurityContextHolderStrategy

考虑以下代码块:

UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword());
Authentication authentication = this.authenticationManager.authenticate(token);
// ...
SecurityContext context = SecurityContextHolder.createEmptyContext();// 1
context.setAuthentication(authentication);// 2
SecurityContextHolder.setContext(context);// 3
  1. 通过静态访问SecurityContextHolder来创建一个空的SecurityContext实例。
  2. 设置SecurityContext实例中的Authentication对象。
  3. 静态设置SecurityContextHolder中的SecurityContext实例。

虽然上面的代码运行良好,但它可能会产生一些不希望的效果:当组件通过SecurityContextHolder静态访问SecurityContext时,当有多个应用程序上下文想要指定SecurityContextHolderStrategy时,这可能会创建竞争条件。这是因为在SecurityContextHolder中,每个类加载器有一个策略,而不是每个应用程序上下文有一个。

为了解决这个问题,组件可以从应用程序上下文连接SecurityContextHolderStrategy。默认情况下,他们仍然会从SecurityContextHolder中查找策略。

这些更改主要是在内部进行的,但是它们为应用程序提供了自动使用SecurityContextHolderStrategy而不是静态访问SecurityContext的机会。为此,您应该将代码更改为以下内容:

public class SomeClass {private final SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder.getContextHolderStrategy();public void someMethod() {UsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.unauthenticated(loginRequest.getUsername(), loginRequest.getPassword());Authentication authentication = this.authenticationManager.authenticate(token);// ...SecurityContext context = this.securityContextHolderStrategy.createEmptyContext();// 1context.setAuthentication(authentication);// 2this.securityContextHolderStrategy.setContext(context);// 3}}
  1. 使用配置的SecurityContextHolderStrategy创建一个空的SecurityContext实例。
  2. 在SecurityContext实例中设置Authentication对象。
  3. 在SecurityContextHolderStrategy中设置SecurityContext实例。

Forcing Eager Session Creation

(强制创建急切会话)

有时候,急切地创建会话是很有价值的。这可以通过使用ForceEagerSessionCreationFilter来实现,它可以这样配置:

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) {http.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.ALWAYS));return http.build();
}

What to read next

(接下来要读什么)

  • 与Spring Session的集群会话
  1. SessionManagementFilter不会检测到在身份验证之后执行重定向的机制进行的身份验证(例如表单登录),因为在身份验证请求期间不会调用过滤器。在这些情况下,会话管理功能必须单独处理。

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

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

相关文章

网络原理TCP/IP(4)

文章目录 面向字节流粘包问题异常情况TCP小结 面向字节流 创建⼀个TCP的socket,同时在内核中创建⼀个发送缓冲区和⼀个接收缓冲区; • 调⽤write时,数据会先写⼊发送缓冲区中; • 如果发送的字节数太⻓,会被拆分成多个TCP的数据包发出; • 如果发送的字节数太短,就会先在缓…

【lesson40】理解文件系统

文章目录 问题磁盘结构&#xff08;物理&#xff09;磁盘的存储结构磁盘的抽象结构 问题 1.有没有没有被打开的文件&#xff1f;当然有 在哪里呢&#xff1f;磁盘中也叫磁盘级文件 2.学习磁盘级别文件的侧重点在哪&#xff1f; 站在单个文件角度: 这个文件在哪里&#xff1f…

【Linux】权限管理

&#x1f525;博客主页&#xff1a; 小羊失眠啦. &#x1f3a5;系列专栏&#xff1a;《C语言》 《数据结构》 《C》 《Linux》 《Cpolar》 ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 文章目录 一 、Linux中的用户1.1 Linux用户分类1.2 用户转换1.3 指令提权 二、Linux权限管…

LeetCode:88. 合并两个有序数组(双指针 Java)

目录 88. 合并两个有序数组 题目描述&#xff1a; 实现代码与解析&#xff1a; 双指针 原理思路&#xff1a; 88. 合并两个有序数组 题目描述&#xff1a; 给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2&#xff0c;另有两个整数 m 和 n &#xff0c;分别表示 num…

vscode git stash apply stash@{1}不生效

解决办法 在stash{1}前后加上引号 git stash apply “stash{1}“即可成功恢复指定的stash

企业网络采用SD-WAN的优势

近年来&#xff0c;SD-WAN成为企业网络领域的一项热门技术&#xff0c;为传统网络带来了新的变革。SD-WAN&#xff08;Software Defined Wide Area Network&#xff0c;软件定义广域网&#xff09;以其灵活性、可管理性和低成本而备受青睐。它不仅能够创建成熟的专用网络&#…

MySQL 教程 2.3

MySQL DELETE 语句 你可以使用 DELETE FROM 命令来删除 MySQL 数据表中的记录。 你可以在 mysql> 命令提示符或 PHP 脚本中执行该命令。 语法 以下是 DELETE 语句从 MySQL 数据表中删除数据的通用语法&#xff1a; DELETE FROM table_name WHERE condition; 参数说明&…

IP风险画像在企业网络安全中应用

随着企业数字化的不断深入&#xff0c;网络安全问题日益突显。IP风险画像作为一种综合性的网络安全工具&#xff0c;为企业提供了更全面的风险评估和防范手段。本文将结合一个实际案例&#xff0c;深入探讨IP风险画像在企业网络安全中的成功应用。 案例背景 一家大型金融机构…

Like his father, he was driving a car called Bluebird. chatGPT学英语

chatGPT学英语 1、翻译为中文&#xff1a;像他父亲一样&#xff0c;他也在开一辆叫做“蓝鸟”的车。 2、时态分析&#xff1a;这是一个过去进行时的句子&#xff0c;句子语法是正确的。 3、句子结构分析&#xff1a; 主句&#xff1a;he was driving a car called Bluebird.…

IP数据云识别真实IP与虚假流量案例

随着互联网的普及&#xff0c;企业在数字领域面临着越来越复杂的网络威胁。为了保护网站免受虚假流量和恶意攻击的影响&#xff0c;许多企业正在采用IP数据云。本文将结合一个真实案例&#xff0c;深入探讨IP数据云如何成功准确地识别真实用户IP和虚假流量IP&#xff0c;提高网…

Oracle 面试题 | 09.精选Oracle高频面试题

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

vue3 之 组合式API—reactive和ref函数

ref&#xff08;&#xff09; 作用&#xff1a;接收简单类型或者对象类型的数据传入并返回一个响应式的对象 核心步骤&#xff1a; 1️⃣ 从 vue 包中导入 ref 函数 2️⃣在 <script setup>// 导入import { ref } from vue// 执行函数 传入参数 变量接收const count …

【Python】【完整代码】解析Excel 文件中的内容并检查是否包含某字符串,并返回判断结果

示例&#xff1a; 开发需求&#xff1a;解析Excel 文件中的内容并检查是否包含 "Fail" 字符&#xff0c;若没有则返回True&#xff0c;若有则返回False 实现代码&#xff1a; #!/usr/bin/env python3 # -*- encoding: utf-8 -*-File : check_excel_for_fail.py Ti…

记录首次使用yolov8-obb

1.数据格式 之前使用的数据格式是yolov5_obb的数据格式&#xff0c;然后需要转数据格式&#xff1a; 目前的数据只支持四个坐标点标注的数据&#xff0c;参考&#xff1a;If a corner of the rotate rectangle is out of the image range, How to annotate the image? Issu…

【如何快速上手Vue.js框架——详细介绍】

如何快速上手Vue.js框架——详细介绍 1. 介绍2. 理解Vue.js的核心概念3. 搭建开发环境4. 创建第一个项目5. 学习基础6. 进阶概念7. 最佳实践和模式8. 构建和部署9. 持续学习10. 实际操作 1. 介绍 要快速上手Vue.js框架&#xff0c;可以按照以下步骤进行学习和实践&#xff1a;…

vue介绍和使用

一、vue介绍 vue官网说&#xff1a;Vue.js&#xff08;读音 /vjuː/&#xff0c;类似于 view&#xff09; 是一套构建用户界面的渐进式的JavaScript框架。与其他重量级框架不同的是&#xff0c;Vue 采用自底向上增量开发的设计。 1、渐进式的JavaScript框架 复制代码 Angular…

[ubuntu]add-apt-repository 添加以及移除

add-apt-repository是一个用于添加PPA&#xff08;Personal Package Archive&#xff09;存储库的命令。它是Ubuntu和基于Ubuntu的Linux发行版中的apt软件包管理系统的一部分。 PPA存储库允许用户安装和更新软件包&#xff0c;这些软件包不包含在官方软件源中。通过添加PPA存储…

OJ_找位置

题干 代码 #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<vector> #include<algorithm> #include<map> using namespace std;int main() {char str[200] { 0 };scanf("%s", str);map<char, vector<int>> times…

openlayers加载天地图

申请天地图key 官方&#xff1a;https://www.tianditu.gov.cn/ 申请key&#xff1a;https://sso.tianditu.gov.cn/login?servicehttps%3A%2F%2Fconsole.tianditu.gov.cn%2F 进去之后&#xff0c;先登录&#xff0c;如果没账号先注册一个就行。 可以创建个应用&#xff0c;…

如何将图片提取文字转换成文字?3个方法帮你转换

如何将图片提取文字转换成文字&#xff1f;在日常生活中&#xff0c;将图片中的文字转换成可编辑的文本可以带来很多便利。例如&#xff0c;我们可以使用OCR技术将书籍或报纸上的文章转换成电子文本&#xff0c;方便阅读和编辑。此外&#xff0c;将名片、菜单、广告等物品上的文…