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

Spring Authorization Server登出说明与实践

本章内容概览

  1. Spring Security提供的/logout登出接口做了什么与如何自定义。
  2. Spring Authorization Server提供的/connect/logout登出接口做了什么与如何自定义。
  3. 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拦截处理的,在过滤器内分别调用了LogoutHandlerLogoutSuccessHandler来处理登出的逻辑和登出成功的逻辑,跟踪源码可知具体处理登出的是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_hintOAuth2授权登录以后获取的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提供的内置端点都是这个流程,支持自定义的地方基本就是ConverterProvider、成功响应和失败响应处理,详见文档oidc-logout-endpoint

在端点的过滤器链配置中添加如下配置

http.getConfigurer(OAuth2AuthorizationServerConfigurer.class)// 开启OpenID Connect 1.0协议相关端点.oidc(oidc -> oidc.logoutEndpoint(logoutEndpoint -> logoutEndpoint.logoutResponseHandler((request, response, authorization) -> {})))

具体的实现就不写了,大家知道入口以后根据自己的业务实现登出即可。

注意:如果要使用这个接口,并自定义logoutResponseHandler逻辑请注销Session,因为默认的ConverterProvider都没有注销当前的Session

注意:如果要使用这个接口,并自定义logoutResponseHandler逻辑请注销Session,因为默认的ConverterProvider都没有注销当前的Session

注意:如果要使用这个接口,并自定义logoutResponseHandler逻辑请注销Session,因为默认的ConverterProvider都没有注销当前的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认证请求头,usernamepassword分别代表客户端的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过滤器一样,可以自定义ConverterProvider、成功响应和失败响应处理,详见文档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连网情况下&#xff0c;浏览器访问地址显示“你尚未连接&#xff0c;代理服务器可能有问题&#xff0c;或地址不正确。”出现如下画面&#xff1a; 2 解决方法 途径1 控制面板-->网络与internet-->internet选项-->Internet属性-->连接-->…...

泷羽sec:shell编程(9)不同脚本的互相调用和重定向操作

声明&#xff1a; 学习视频来自B站up主 泷羽sec 有兴趣的师傅可以关注一下&#xff0c;如涉及侵权马上删除文章&#xff0c;笔记只是方便各位师傅的学习和探讨&#xff0c;文章所提到的网站以及内容&#xff0c;只做学习交流&#xff0c;其他均与本人以及泷羽sec团队无关&#…...

Milvus×OPPO:如何构建更懂你的大模型助手

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

单片机几大时钟源

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

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 链动模式商城小程序的创新发展与应用研究

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

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的通知机制是什么?

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

嵌入式蓝桥杯学习1 电量LED

cubemx配置 1.新建一个STM32G431RBT6文件 2.在System-Core中点击SYS&#xff0c;找到Debug&#xff08;设置为Serial Wire&#xff09; 3.在System-Core中点击RCC&#xff0c;找到High Speed Clock(设置为Crystal/Ceramic Resonator) 4.打开Clock Configuration &#xff0…...

bsmap输出结果解释

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

【java-数据结构篇】揭秘 Java LinkedList:链表数据结构的 Java 实现原理与核心概念

我的个人主页 我的专栏&#xff1a;Java-数据结构&#xff0c;希望能帮助到大家&#xff01;&#xff01;&#xff01;点赞❤ 收藏❤ 目录 1. Java LinkedList 基础 1.1 LinkedList 简介 1.2 LinkedList 的实现原理 1.3 LinkedList 与 ArrayList 的区别 2. 链表基础 2.1 链…...

macOS运行amd64的镜像

在macOS上运行amd64&#xff08;x86_64&#xff09;架构的镜像&#xff0c;通常通过虚拟化或仿真工具来实现。例如&#xff0c;如果你使用的是基于Apple Silicon&#xff08;M1或M2等&#xff09;芯片的Mac&#xff0c;那么你的处理器是ARM架构的&#xff0c;而amd64是x86架构&…...

轻量的基于图结构的RAG方案LightRAG

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

计算机的错误计算(一百七十三)

摘要 给定多项式 在 MATLAB 中计算 的值。输出是错误结果。 例1. 已知 计算 直接贴图吧&#xff1a; 这样&#xff0c;MATLAB 输出了错误结果。因为准确值为 0.2401e-16 . 注&#xff1a;可参看计算机的错误计算&#xff08;六&#xff09;。...

【力扣】—— 二叉树的前序遍历、字典序最小回文串

Hi~&#xff01;这里是奋斗的明志&#xff0c;很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~~ &#x1f331;&#x1f331;个人主页&#xff1a;奋斗的明志 &#x1f331;&#x1f331;所属专栏&#xff1a;数据结构 &#x1f4da;本系列文章为个人学…...

linux替换更高版本gcc

实际使用时对与gcc版本有很多要求, 需要在centos上安装更高版本的gcc 1、安装centos-release-scl sudo yum install centos-release-scl2、安装devtoolset&#xff0c;注意&#xff0c;如果想安装7.版本的&#xff0c;就改成devtoolset-7-gcc&#xff0c;以此类推 sudo yum …...

在Java中使用Apache POI导入导出Excel(六)

本文将继续介绍POI的使用&#xff0c;上接在Java中使用Apache POI导入导出Excel&#xff08;五&#xff09; 使用Apache POI组件操作Excel&#xff08;六&#xff09; 43、隐藏和取消隐藏行 使用 Excel&#xff0c;可以通过选择该行&#xff08;或行&#xff09;来隐藏工作表…...

`uni.setClipboardData` 是 uni-app 提供的一个 API 设置系统剪贴板的内容

uni.setClipboardData是uni-app提供的一个API&#xff0c;用于设置系统剪贴板的内容。 使用说明&#xff1a; 使用此API可以将指定的文本内容复制到系统剪贴板&#xff0c;使用户能够在其他应用或页面中粘贴这些内容。 uni.setClipboardData({data: , // 需要复制的内容 suc…...

【大模型微调】pdf转markdown

目前市面上大部分都是pdf文档,要想转换成能训练的文本,调研了各种工具。 觉得MinerU确实不错。 参考此链接进行操作 MinerU/docs/README_Ubuntu_CUDA_Acceleration_en_US.md at master opendatalab/MinerU GitHub 需要注意的几个点: 1. 使用root账户安装的,配置文件在…...

RestClient

什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端&#xff0c;它允许HTTP与Elasticsearch 集群通信&#xff0c;而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级&#xff…...

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする 1、前言(1)情况说明(2)工程师的信仰2、知识点(1) にする1,接续:名词+にする2,接续:疑问词+にする3,(A)は(B)にする。(2)復習:(1)复习句子(2)ために & ように(3)そう(4)にする3、…...

《Playwright:微软的自动化测试工具详解》

Playwright 简介:声明内容来自网络&#xff0c;将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具&#xff0c;支持 Chrome、Firefox、Safari 等主流浏览器&#xff0c;提供多语言 API&#xff08;Python、JavaScript、Java、.NET&#xff09;。它的特点包括&a…...

MODBUS TCP转CANopen 技术赋能高效协同作业

在现代工业自动化领域&#xff0c;MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步&#xff0c;这两种通讯协议也正在被逐步融合&#xff0c;形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...

Psychopy音频的使用

Psychopy音频的使用 本文主要解决以下问题&#xff1a; 指定音频引擎与设备&#xff1b;播放音频文件 本文所使用的环境&#xff1a; Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...

PostgreSQL——环境搭建

一、Linux # 安装 PostgreSQL 15 仓库 sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-$(rpm -E %{rhel})-x86_64/pgdg-redhat-repo-latest.noarch.rpm# 安装之前先确认是否已经存在PostgreSQL rpm -qa | grep postgres# 如果存在&#xff0…...

tauri项目,如何在rust端读取电脑环境变量

如果想在前端通过调用来获取环境变量的值&#xff0c;可以通过标准的依赖&#xff1a; std::env::var(name).ok() 想在前端通过调用来获取&#xff0c;可以写一个command函数&#xff1a; #[tauri::command] pub fn get_env_var(name: String) -> Result<String, Stri…...

MyBatis中关于缓存的理解

MyBatis缓存 MyBatis系统当中默认定义两级缓存&#xff1a;一级缓存、二级缓存 默认情况下&#xff0c;只有一级缓存开启&#xff08;sqlSession级别的缓存&#xff09;二级缓存需要手动开启配置&#xff0c;需要局域namespace级别的缓存 一级缓存&#xff08;本地缓存&#…...

学习一下用鸿蒙​​DevEco Studio HarmonyOS5实现百度地图

在鸿蒙&#xff08;HarmonyOS5&#xff09;中集成百度地图&#xff0c;可以通过以下步骤和技术方案实现。结合鸿蒙的分布式能力和百度地图的API&#xff0c;可以构建跨设备的定位、导航和地图展示功能。 ​​1. 鸿蒙环境准备​​ ​​开发工具​​&#xff1a;下载安装 ​​De…...

rknn toolkit2搭建和推理

安装Miniconda Miniconda - Anaconda Miniconda 选择一个 新的 版本 &#xff0c;不用和RKNN的python版本保持一致 使用 ./xxx.sh进行安装 下面配置一下载源 # 清华大学源&#xff08;最常用&#xff09; conda config --add channels https://mirrors.tuna.tsinghua.edu.cn…...