Spring Authorization Server登出说明与实践
本章内容概览
Spring Security
提供的/logout
登出接口做了什么与如何自定义。Spring Authorization Server
提供的/connect/logout
登出接口做了什么与如何自定义。Spring Authorization Server
提供的/oauth2/revoke
撤销token接口做了什么与如何自定义。
前言
既然系统中有登录功能,那么必然也会有登出的需要,登出的效果一般都是清除认证相关的Session
,同时也要让access token
无效化,换而言之就是清除当前用户认证相关的状态,使用户在项目中回到未认证状态,因为表单登录以后存储在Session
中的状态和请求携带access token
访问后端时都代表着用户的认证信息,有了这些认证信息才代表着用户登录过,可以继续接下来的操作。
本章基本上是理论与实验性的内容,编码内容很少,内容可能也会比较多,请大家耐心观看。
Spring Security
提供的/logout
访问方式
浏览器直接访问http://127.0.0.1:8080/logout
(地址栏直接输入该地址后按回车)
其中127.0.0.1
是认证服务所在服务器的ip,8080
是认证服务对外提供服务的端口。
分析
该接口是Security
提供的一个接口,该接口是过滤器LogoutFilter
拦截处理的,在过滤器内分别调用了LogoutHandler
与LogoutSuccessHandler
来处理登出的逻辑和登出成功的逻辑,跟踪源码可知具体处理登出的是SecurityContextLogoutHandler
,在该类中根据当前请求获取Http Session
并销毁,之后清除SecurityContextRepository
中保存的认证信息,之后就是登出成功的处理了,处理登出成功的是SimpleUrlLogoutSuccessHandler
,该类中会默认重定向至defaultTargetUrl
属性的值(应该为url),默认情况下启动时在加载LogoutFilter
时会初始化一个SimpleUrlLogoutSuccessHandler
的实例给登出过滤器使用,默认会设置defaultTargetUrl
属性的值为/login?logout
;大致逻辑就是这样。
自定义
根据上边的分析可知有两个很明显的扩展点,一个是登出处理,一个是登录成功处理,大部分情况下只需要自定义登录成功处理即可,那么该如何配置?请看下方示例代码
在认证相关的过滤器中添加如下代码
// 自定义登出配置
http.logout(logout -> logout// 定义登出路径.logoutUrl("/logout")// 登出时同步撤销Token.addLogoutHandler(new RevokeAccessTokenLogoutHandler(serverProperties))// 自定义登出成功响应处理.logoutSuccessHandler(new JsonLogoutSuccessHandler())
);
RevokeAccessTokenLogoutHandler
的具体实现
package com.example.authorization.handler;import com.example.model.Result;
import com.example.util.JsonUtils;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.http.*;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.ObjectUtils;
import org.springframework.web.client.RestTemplate;import java.nio.charset.StandardCharsets;/*** 登出处理-撤销token** @author vains*/
@Slf4j
@RequiredArgsConstructor
public class RevokeAccessTokenLogoutHandler implements LogoutHandler {private final ServerProperties serverProperties;private final AuthorizationServerSettings authorizationServerSettings;private final RestTemplate restTemplate = new RestTemplate();@Override@SneakyThrowspublic void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {// 获取token(可以是Access Token,也可以是Refresh Token)String token = request.getParameter(OAuth2ParameterNames.TOKEN);if (ObjectUtils.isEmpty(token)) {// 写回json数据Result<String> result = Result.error("token is empty");response.setCharacterEncoding(StandardCharsets.UTF_8.name());response.setContentType(MediaType.APPLICATION_JSON_VALUE);response.getWriter().write(JsonUtils.objectCovertToJson(result));response.getWriter().flush();response.getWriter().close();return;}// 撤销token逻辑Integer port = serverProperties.getPort();// 撤销token地址String revokeUrl = "http://127.0.0.1:"+ (port == null ? "8080" : port)// 默认是/oauth2/revoke+ authorizationServerSettings.getTokenRevocationEndpoint();MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();formData.add(OAuth2ParameterNames.TOKEN, token);// 准备请求的headerHttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);headers.add(HttpHeaders.AUTHORIZATION, "Basic " + HttpHeaders.encodeBasicAuth("messaging-client", "123456", StandardCharsets.UTF_8));// 封装请求HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<>(formData, headers);// 撤销token,默认无响应try {ResponseEntity<String> responseEntity = restTemplate.postForEntity(revokeUrl, entity, String.class);if (responseEntity.getStatusCode().is2xxSuccessful()) {log.info("revoked access token");}} catch (Exception e) {log.warn("revoked access token failed, cause: {}", e.getMessage());// 撤销token失败写回json数据Result<String> result = Result.error("revoked access token failed, cause: " + e.getMessage());response.setCharacterEncoding(StandardCharsets.UTF_8.name());response.setContentType(MediaType.APPLICATION_JSON_VALUE);response.getWriter().write(JsonUtils.objectCovertToJson(result));response.getWriter().flush();response.getWriter().close();}}}
JsonLogoutSuccessHandler
的具体实现,可以在该类中响应json或自己实现重定向逻辑
package com.example.authorization.handler;import com.example.model.Result;
import com.example.util.JsonUtils;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;import java.io.IOException;
import java.nio.charset.StandardCharsets;/*** 登出成功自定义处理** @author vains*/
@Slf4j
public class JsonLogoutSuccessHandler implements LogoutSuccessHandler {@Overridepublic void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {log.info("onLogoutSuccessHandler");// 写回json数据Result<String> result = Result.success();response.setCharacterEncoding(StandardCharsets.UTF_8.name());response.setContentType(MediaType.APPLICATION_JSON_VALUE);response.getWriter().write(JsonUtils.objectCovertToJson(result));response.getWriter().flush();// 或重定向至某个url// 重定向操作...略}}
这样就可以在销毁当前Session的情况下撤销token了,如图
未带token参数示例,如图
现在就完成了销毁Session与撤销token的步骤。
Spring Authorization Server
提供的/connect/logout
该接口是Openid Connect 1.0 协议中规定的一个接口。
访问方式
浏览器直接访问http://127.0.0.1:8080/connect/logout?id_token_hint={id_token}&post_logout_redirect_uri={客户端信息中配置的post_logout_redirect_uri}
(地址栏直接输入该地址后按回车)
参数说明:
参数 | 描述 | 描述 |
---|---|---|
id_token_hint | OAuth2授权登录以后获取的id token | 协议中是推荐传入,但是在Authorization Server中必填 |
post_logout_redirect_uri | 客户端信息中配置的post_logout_redirect_uri | 登出后重定向的地址,不传会走默认登出处理 |
更多参数说明详见:spec openid-connect-rpinitiated-1_0 RPLogout
其中127.0.0.1
是认证服务所在服务器的ip,8080
是认证服务对外提供服务的端口。
分析
该接口是Spring Authorization Server
基于Openid Connect 1.0
协议提供的一个OIDC登出接口,接口由OidcLogoutEndpointFilter
拦截处理,在拦截器中先调用OidcLogoutAuthenticationConverter
获取请求入参并转成OidcLogoutAuthenticationToken
返回,之后过滤器再调用AuthenticationManager(ProviderManager)
来做登出处理,根据OidcLogoutAuthenticationToken
最终由OidcLogoutAuthenticationProvider
处理具体的登出逻辑,在该provider中没有针对Session
或者access token
做什么操作,先根据id_token_hint
参数查询oauth2登录生成的认证信息,之后针对认证信息与Session做一些校验,限制了必须是登录时的Session才可以调用登出;之后就是登出成功处理了,上述所说的登出后跳转路径以及销毁当前Session
都是在这个登出成功处理中操作的,如果当前Session存在并且获取到表单登录生成的认证信息则调用SecurityContextLogoutHandler
处理,这个和上边的/logout
登出使用的LogoutHandler
一样,逻辑是一样的,都是销毁当前Session与认证信息,接下来是跳转的操作,如果请求/connect/logout
时携带了post_logout_redirect_uri
参数,则会跳转到该url,如果没有携带该参数,则会通过SimpleUrlLogoutSuccessHandler
跳转到根目录。
自定义
通过上述流程可以发现这个是与/oauth2/token
和/oauth2/authorize
类似的一个接口,或者说Spring Authorization Server
提供的内置端点都是这个流程,支持自定义的地方基本就是Converter
、Provider
、成功响应和失败响应处理,详见文档oidc-logout-endpoint
在端点的过滤器链配置中添加如下配置
http.getConfigurer(OAuth2AuthorizationServerConfigurer.class)// 开启OpenID Connect 1.0协议相关端点.oidc(oidc -> oidc.logoutEndpoint(logoutEndpoint -> logoutEndpoint.logoutResponseHandler((request, response, authorization) -> {})))
具体的实现就不写了,大家知道入口以后根据自己的业务实现登出即可。
注意:如果要使用这个接口,并自定义logoutResponseHandler
逻辑请注销Session
,因为默认的Converter
和Provider
都没有注销当前的Session
!
注意:如果要使用这个接口,并自定义logoutResponseHandler
逻辑请注销Session
,因为默认的Converter
和Provider
都没有注销当前的Session
!
注意:如果要使用这个接口,并自定义logoutResponseHandler
逻辑请注销Session
,因为默认的Converter
和Provider
都没有注销当前的Session
!
如果想在Provider中注销Session也可以,也可以在里边撤销token(撤销token代码请参考上边的RevokeAccessTokenLogoutHandler
),提供了很多配置入口,自由度也是比较高的。
不带post_logout_redirect_uri
参数访问效果,会清除Session并跳转到根目录/
,没有放行直接跳转到登录页面了(代表着认证信息与Session都被清除了)
带post_logout_redirect_uri
参数访问效果,会清除Session并跳转到post_logout_redirect_uri
参数的值(一个url)。
请注意,这个url必须要在客户端信息配置中存在,否则会提示错误!
请注意,这个url必须要在客户端信息配置中存在,否则会提示错误!
请注意,这个url必须要在客户端信息配置中存在,否则会提示错误!
如果集成了十九章的内容,将客户端信息存储在redis,则改代码后重启即可,如果是存储在数据库的请在重启后检查数据库中的数据是否被修改。
Spring Authorization Server
提供的/oauth2/revoke
该接口是基于RFC 7009实现的一个接口。
访问方式
curl --location --globoff --request POST 'http://127.0.0.1:8080/oauth2/revoke?token={access token或者refresh token}' \
--header 'Authorization: Basic bWVzc2FnaW5nLWNsaWVudDoxMjM0NTY='
token参数是想撤销的token(access token\refresh token
)
其中127.0.0.1
是认证服务所在服务器的ip,8080
是认证服务对外提供服务的端口。
访问时记得添加basic认证
请求头,username
和password
分别代表客户端的id和密钥
更多参数及参数的详细说明见RFC 7009 Revocation Request
分析
该接口是Spring Authorization Server
基于OAuth2.1
协议提供的一个token撤销接口,接口由OAuth2TokenRevocationEndpointFilter
拦截处理,在过滤器内的逻辑与上边提到的OidcLogoutEndpointFilter
过滤器的大致流程一致,流程是:通过Converter
转换请求参数,然后由Provider
将token的状态改为无效状态,最后是处理成功的handler;根据源码可知是OAuth2TokenRevocationAuthenticationConverter
来将撤销请求的参数转为OAuth2TokenRevocationAuthenticationToken
,然后ProviderManager
将生成的OAuth2TokenRevocationAuthenticationToken
交由OAuth2TokenRevocationAuthenticationProvider
处理,在该provider
内将token的状态改为无效状态,默认的成功处理只设置了http 200
的状态码,无任何响应。
这个接口在讲上边的登出接口时都有提过,上边两个接口主要目的是清除认证状态
与销毁当前Session
,但是oauth2登录生成access token/refresh token
还可以继续使用,所以需要调用当前接口来将token无效化。
自定义
跟上边的OidcLogoutEndpointFilter
过滤器一样,可以自定义Converter
、Provider
、成功响应和失败响应处理,详见文档oauth2-token-revocation-endpoint
为避免贴大量代码我这边就只自定义一个成功响应处理,在端点的过滤器链配置中添加如下代码
http.getConfigurer(OAuth2AuthorizationServerConfigurer.class)// 开启OpenID Connect 1.0协议相关端点.oidc(Customizer.withDefaults())// 自定义token撤销断点配置入口.tokenRevocationEndpoint(tokenRevocationEndpoint -> tokenRevocationEndpoint// 撤销成功响应.revocationResponseHandler(new TokenRevocationSuccessHandler()));
TokenRevocationSuccessHandler
实现如下,成功后响应json
package com.example.authorization.handler;import com.example.model.Result;
import com.example.util.JsonUtils;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;import java.io.IOException;
import java.nio.charset.StandardCharsets;/*** token撤销成功处理** @author vains*/
@Slf4j
public class TokenRevocationSuccessHandler implements AuthenticationSuccessHandler {@Overridepublic void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {log.info("Token revocation success");// 写回json数据Result<String> result = Result.success();response.setCharacterEncoding(StandardCharsets.UTF_8.name());response.setContentType(MediaType.APPLICATION_JSON_VALUE);response.getWriter().write(JsonUtils.objectCovertToJson(result));response.getWriter().flush();}
}
携带access token访问测试
前后端分离中想用axios请求/logout
和/connect/logout
?
这种方式仅适用于使用Spring Session
将前后端的Session一同管理,前后端共享Session以后才能使用这种方式,并且初始化axios时需要添加withCredentials: true
配置,使其在跨域请求时带上cookie,这样后端才能获取到登录时的Session并进行销毁。(未测试,应该可行)
如果没有使用Spring Session
将前后端的Session一同管理,推荐使用文中的方式,打开一个新标签页并访问登出接口,因为在上边的分析中都讲过是获取当前Session并销毁,如果使用axios访问那获取不到登录时的Session则会跳过销毁Session的步骤。
Jwt token为什么撤销了以后还能继续使用?
因为jwt是无状态的,资源服务器解析时不会从认证服务查询token的状态,所以在解析时不能知道jwt token的状态,如果想在撤销token以后使jwt token失效可以在redis中添加一个黑名单,解析时查看jwt类型的access token是否在黑名单中,以防止撤销后的token继续使用。
最后
到此就说明了两个用户登出和一个token撤销的接口,里边的逻辑有些可能没有讲到,但是处理请求的类,以及具体逻辑的处理,相关的类我基本上都贴出来,大家可以熟悉一下源码,然后看看怎么结合自己业务来实现登出相关的内容。
附录
新仓库地址
spec openid-connect-rpinitiated-1_0 RPLogout
oidc-logout-endpoint
RFC 7009
RFC 7009 Revocation Request
oauth2-token-revocation-endpoint
相关文章:

Spring Authorization Server登出说明与实践
本章内容概览 Spring Security提供的/logout登出接口做了什么与如何自定义。Spring Authorization Server提供的/connect/logout登出接口做了什么与如何自定义。Spring Authorization Server提供的/oauth2/revoke撤销token接口做了什么与如何自定义。 前言 既然系统中有登录功…...

浏览器报错 | 代理服务器可能有问题,或地址不正确
1 问题描述 Windows连网情况下,浏览器访问地址显示“你尚未连接,代理服务器可能有问题,或地址不正确。”出现如下画面: 2 解决方法 途径1 控制面板-->网络与internet-->internet选项-->Internet属性-->连接-->…...

泷羽sec:shell编程(9)不同脚本的互相调用和重定向操作
声明: 学习视频来自B站up主 泷羽sec 有兴趣的师傅可以关注一下,如涉及侵权马上删除文章,笔记只是方便各位师傅的学习和探讨,文章所提到的网站以及内容,只做学习交流,其他均与本人以及泷羽sec团队无关&#…...

Milvus×OPPO:如何构建更懂你的大模型助手
01. 背景 AI业务快速增长下传统关系型数据库无法满足需求。 2024年恰逢OPPO品牌20周年,OPPO也宣布正式进入AI手机的时代。超千万用户开始通过例如通话摘要、新小布助手、小布照相馆等搭载在OPPO手机上的应用体验AI能力。 与传统的应用不同的是,在AI驱动的…...

单片机几大时钟源
在单片机中,MSI、HSI和HSE通常指的是用于内部晶振配置的不同功能模块: MSI (Master Oscillator System Interface):这是最低级的一种时钟源管理单元,它控制着最基本的系统时钟(SYSCLK),一般由外…...

reverse学习总结(12)
一.[FlareOn4]IgniteMe1 https://files.buuoj.cn/files/02b39b8efca02367af23aa279c81cbec/attachment.zip 根据汇编语言分析 查看需要返回为1的函数 int sub_401050() {int v1; // [esp0h] [ebp-Ch]int i; // [esp4h] [ebp-8h]unsigned int j; // [esp4h] [ebp-8h]char v4; …...

基于“微店 Park”模式下 2+1 链动模式商城小程序的创新发展与应用研究
摘要:本文以“微店 Park”从“开店工具”向“众创平台”的转型为背景,深入探讨 21 链动模式商城小程序在该平台情境下的应用潜力与创新发展路径。通过剖析“微店 Park”的运营模式,包括灵活承租、低成本入驻、多元流量引流等特点,…...

C++11:【列表初始化】【右值引用和移动语义】
目录 一.列表初始化 1.1 C98传统的{} 1.2C11中的{} 1.3C中的std::initializer_list 二.右值引用和移动语义 2.1左值和右值 2.2左值引用和右值引用 2.3引用延长生命周期 2.4左值和右值的参数匹配 2.5右值引用和移动语义的使用场景 2.5.1左值引用主要使用场景 2.5.2移…...

Zookeeper的通知机制是什么?
大家好,我是锋哥。今天分享关于【Zookeeper的通知机制是什么?】面试题。希望对大家有帮助; Zookeeper的通知机制是什么? 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 Zookeeper的通知机制主要通过Watcher实现,它是Zookeeper客…...

嵌入式蓝桥杯学习1 电量LED
cubemx配置 1.新建一个STM32G431RBT6文件 2.在System-Core中点击SYS,找到Debug(设置为Serial Wire) 3.在System-Core中点击RCC,找到High Speed Clock(设置为Crystal/Ceramic Resonator) 4.打开Clock Configuration ࿰…...

bsmap输出结果解释
关于, , -, --的解释 对应着参考基因组的正链(有义链,非模板链,即hg38的序列,watson链); -代表正链的互补链(正常情况下正链的互补链是负链,但在重硫酸盐处理后正链和负链并不互补…...

【java-数据结构篇】揭秘 Java LinkedList:链表数据结构的 Java 实现原理与核心概念
我的个人主页 我的专栏:Java-数据结构,希望能帮助到大家!!!点赞❤ 收藏❤ 目录 1. Java LinkedList 基础 1.1 LinkedList 简介 1.2 LinkedList 的实现原理 1.3 LinkedList 与 ArrayList 的区别 2. 链表基础 2.1 链…...
macOS运行amd64的镜像
在macOS上运行amd64(x86_64)架构的镜像,通常通过虚拟化或仿真工具来实现。例如,如果你使用的是基于Apple Silicon(M1或M2等)芯片的Mac,那么你的处理器是ARM架构的,而amd64是x86架构&…...

轻量的基于图结构的RAG方案LightRAG
LightRAG出自2024年10月的论文《LIGHTRAG: SIMPLE AND FASTRETRIEVAL-AUGMENTED GENERATION》(github),也是使用图结构来索引和搜索相关文本。 LightRAG作者认为已有的RAG系统有如下两个限制,导致难以回答类似"How does the rise of electric vehi…...

计算机的错误计算(一百七十三)
摘要 给定多项式 在 MATLAB 中计算 的值。输出是错误结果。 例1. 已知 计算 直接贴图吧: 这样,MATLAB 输出了错误结果。因为准确值为 0.2401e-16 . 注:可参看计算机的错误计算(六)。...

【力扣】—— 二叉树的前序遍历、字典序最小回文串
Hi~!这里是奋斗的明志,很荣幸您能阅读我的文章,诚请评论指点,欢迎欢迎 ~~ 🌱🌱个人主页:奋斗的明志 🌱🌱所属专栏:数据结构 📚本系列文章为个人学…...
linux替换更高版本gcc
实际使用时对与gcc版本有很多要求, 需要在centos上安装更高版本的gcc 1、安装centos-release-scl sudo yum install centos-release-scl2、安装devtoolset,注意,如果想安装7.版本的,就改成devtoolset-7-gcc,以此类推 sudo yum …...
在Java中使用Apache POI导入导出Excel(六)
本文将继续介绍POI的使用,上接在Java中使用Apache POI导入导出Excel(五) 使用Apache POI组件操作Excel(六) 43、隐藏和取消隐藏行 使用 Excel,可以通过选择该行(或行)来隐藏工作表…...
`uni.setClipboardData` 是 uni-app 提供的一个 API 设置系统剪贴板的内容
uni.setClipboardData是uni-app提供的一个API,用于设置系统剪贴板的内容。 使用说明: 使用此API可以将指定的文本内容复制到系统剪贴板,使用户能够在其他应用或页面中粘贴这些内容。 uni.setClipboardData({data: , // 需要复制的内容 suc…...
【大模型微调】pdf转markdown
目前市面上大部分都是pdf文档,要想转换成能训练的文本,调研了各种工具。 觉得MinerU确实不错。 参考此链接进行操作 MinerU/docs/README_Ubuntu_CUDA_Acceleration_en_US.md at master opendatalab/MinerU GitHub 需要注意的几个点: 1. 使用root账户安装的,配置文件在…...

iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版分享
平时用 iPhone 的时候,难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵,或者买了二手 iPhone 却被原来的 iCloud 账号锁住,这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...

ESP32读取DHT11温湿度数据
芯片:ESP32 环境:Arduino 一、安装DHT11传感器库 红框的库,别安装错了 二、代码 注意,DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...

智能在线客服平台:数字化时代企业连接用户的 AI 中枢
随着互联网技术的飞速发展,消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁,不仅优化了客户体验,还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用,并…...

Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具
文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...

3-11单元格区域边界定位(End属性)学习笔记
返回一个Range 对象,只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意:它移动的位置必须是相连的有内容的单元格…...
SQL慢可能是触发了ring buffer
简介 最近在进行 postgresql 性能排查的时候,发现 PG 在某一个时间并行执行的 SQL 变得特别慢。最后通过监控监观察到并行发起得时间 buffers_alloc 就急速上升,且低水位伴随在整个慢 SQL,一直是 buferIO 的等待事件,此时也没有其他会话的争抢。SQL 虽然不是高效 SQL ,但…...

AI+无人机如何守护濒危物种?YOLOv8实现95%精准识别
【导读】 野生动物监测在理解和保护生态系统中发挥着至关重要的作用。然而,传统的野生动物观察方法往往耗时耗力、成本高昂且范围有限。无人机的出现为野生动物监测提供了有前景的替代方案,能够实现大范围覆盖并远程采集数据。尽管具备这些优势…...

RSS 2025|从说明书学习复杂机器人操作任务:NUS邵林团队提出全新机器人装配技能学习框架Manual2Skill
视觉语言模型(Vision-Language Models, VLMs),为真实环境中的机器人操作任务提供了极具潜力的解决方案。 尽管 VLMs 取得了显著进展,机器人仍难以胜任复杂的长时程任务(如家具装配),主要受限于人…...
C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)
名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...

TSN交换机正在重构工业网络,PROFINET和EtherCAT会被取代吗?
在工业自动化持续演进的今天,通信网络的角色正变得愈发关键。 2025年6月6日,为期三天的华南国际工业博览会在深圳国际会展中心(宝安)圆满落幕。作为国内工业通信领域的技术型企业,光路科技(Fiberroad&…...