SpringBoot3集成Oauth2.1——5資源地址配置

配置問題說明

如下所示,代碼配置了兩個,過濾器,一個是資源保護,一個是不保護。

	/** @Description: 配置需要保護的資源* @author: 胡濤* @mail: hutao_2017@aliyun.com* @date: 2025年5月23日 下午2:28:20*/@Bean@Order(2)public SecurityFilterChain resourceServerSecurityFilterChain(HttpSecurity http) throws Exception {http.securityMatcher("/sysOrg/info/page").csrf(csrf -> csrf.disable()).authorizeHttpRequests(authorize -> authorize.anyRequest().authenticated()).oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults())).formLogin(form -> form.disable());return http.build();}/** @Description: 配置不需要保護的資源* @author: 胡濤* @mail: hutao_2017@aliyun.com* @date: 2025年5月23日 下午2:28:20*/@Bean@Order(3)public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests(authorize ->authorize.requestMatchers("/sysOrg/info/list",      //測試該接口公開訪問,不需要保護          "/login",                 // 登錄頁面"/logout",                // 登出端點"/swagger-ui/**",         // Swagger UI"/v3/api-docs/**",        // OpenAPI文檔"/webjars/**",            // WebJars資源"/swagger-resources/**",  // Swagger資源"/doc.html"               // Knife4j文檔頁面).permitAll().anyRequest().authenticated()).formLogin(Customizer.withDefaults()).logout(Customizer.withDefaults());return http.build();}

先說上述代碼的結果

訪問http://127.0.0.1:18080/doc.html是正常的,這是期望的
訪問http://127.0.0.1:18080/sysOrg/info/page,會被401,這也是期望的,因為我攜帶無效token
在這里插入圖片描述
但是我訪問http://127.0.0.1:18080/sysOrg/info/list,給我從定向到登錄頁面了
在這里插入圖片描述
什么意思嗯?就是說,下面的配置中,配置的其他靜態資源,例如swagger頁面,是能訪問的,但是里面的/sysOrg/info/list這個接口不能訪問。
在這里插入圖片描述
這個破問題,百度,找官方資料,GPT、各類AI大模型,一點卵用沒得。配置文件,開一下debug,看看

debug: true
logging.level.org.springframework.security: DEBUG

在這里插入圖片描述
接下來當我訪問swagger的時候
在這里插入圖片描述
通過debug的日志,發現正常的。
在這里插入圖片描述
接下來訪問http://127.0.0.1:18080/sysOrg/info/page
在這里插入圖片描述
這也是符合我預期的,因為我的JWT token確實無效

o.s.security.web.FilterChainProxy        : Securing POST /sysOrg/info/page
o.s.s.o.s.r.a.JwtAuthenticationProvider  : Failed to authenticate since the JWT was invalid

但是我訪問http://127.0.0.1:18080/sysOrg/info/list(也就是配置的公開API接口)
在這里插入圖片描述
根據日志報錯,提示無效的CSRF token。然后從定向到登錄了。

o.s.security.web.FilterChainProxy        : Securing POST /sysOrg/info/list
o.s.security.web.csrf.CsrfFilter         : Invalid CSRF token found for http://127.0.0.1:18080/sysOrg/info/list
o.s.s.w.access.AccessDeniedHandlerImpl   : Responding with 403 status code
o.s.security.web.FilterChainProxy        : Securing POST /error
o.s.s.w.a.AnonymousAuthenticationFilter  : Set SecurityContextHolder to anonymous SecurityContext
o.s.s.web.DefaultRedirectStrategy        : Redirecting to http://127.0.0.1:18080/login
o.s.security.web.FilterChainProxy        : Securing GET /login

通過查閱到相關資料,得知

Spring Security 默認對 POSTPUTDELETE 等非 GET 請求啟用 CSRF 校驗,要求請求中包含有效的 CSRF 令牌

于是加了一個get方法的測試接口,果然,不進行驗證了。說明,該版本是需要對CSRF進行驗證
在這里插入圖片描述
接下來,我們對/sysOrg/info/list進行CSRF忽略。

@Bean@Order(3)public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {http.csrf(csrf -> csrf.ignoringRequestMatchers("/sysOrg/info/list"			//測試該接口公開訪問,不需要保護  )).authorizeHttpRequests(authorize ->authorize.requestMatchers("/sysOrg/info/list",      //測試該接口公開訪問,不需要保護          "/sysOrg/info/test/get",      //測試該接口公開訪問,不需要保護          "/login",                 // 登錄頁面"/logout",                // 登出端點"/swagger-ui/**",         // Swagger UI"/v3/api-docs/**",        // OpenAPI文檔"/webjars/**",            // WebJars資源"/swagger-resources/**",  // Swagger資源"/doc.html"               // Knife4j文檔頁面).permitAll().anyRequest().authenticated()).formLogin(Customizer.withDefaults()).logout(Customizer.withDefaults());return http.build();}

在這里插入圖片描述
現在終于能訪問了。而不是重新返回登錄頁面。
接下來,我使用rest ful地址參數,如下所示,很好,又返回登錄頁面了。

	@PostMapping("/info/list/{id}")@ApiOperationSupport(order = 6)public R<List<SysOrg>> ssssssssss(@PathVariable String id) {return R.ok();}

在這里插入圖片描述
Spring Security 的路徑匹配支持 Ant 模式,需使用 ** 或 * 通配符匹配動態路徑
因此我們修改下配置,修改為/sysOrg/info/list/**

	@Bean@Order(3)public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {http.csrf(csrf -> csrf.ignoringRequestMatchers("/sysOrg/info/list/**"		    //測試該接口公開訪問,不需要保護  )).authorizeHttpRequests(authorize ->authorize.requestMatchers("/sysOrg/info/list/**",   //測試該接口公開訪問,不需要保護          "/sysOrg/info/test/get",  //測試該接口公開訪問,不需要保護          "/login",                 // 登錄頁面"/logout",                // 登出端點"/swagger-ui/**",         // Swagger UI"/v3/api-docs/**",        // OpenAPI文檔"/webjars/**",            // WebJars資源"/swagger-resources/**",  // Swagger資源"/doc.html"               // Knife4j文檔頁面).permitAll().anyRequest().authenticated()).formLogin(Customizer.withDefaults()).logout(Customizer.withDefaults());return http.build();}

當然現在在地址后面拼接參數也可以
在這里插入圖片描述

配置思路

截止目前技術路線就打通了,接下來,就是怎么設計了來幫助我們更好的配置接口地址了,總不能在代碼里面挨個的去寫
思路
1.ignore-url: yml配置不需要進行攔截的地址,例如靜態資源文件、swagger、公發API接口
2. allUrl:獲取指定包路徑下的所有的接口地址
3. 配置資源保護API接口:保護的API接口 = allUrl 減 ignore-url
4. 配置公共開發API接口:ignore-url
最后完整的示例代碼如下

配置信息

在這里插入圖片描述

package.path: com.example.demoignore-url: "/login;/logout;/swagger-ui/**;/v3/api-docs/**;/webjars/**;/swagger-resources/**;/doc.html;/code/generate;/table/download;/sysUser/**"

oauth2.1配置

import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.http.MediaType;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.oidc.OidcScopes;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.server.authorization.client.InMemoryRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer;
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
import org.springframework.security.oauth2.server.authorization.settings.ClientSettings;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.WebApplicationContext;import com.example.demo.UrlsUtils;
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;import lombok.extern.log4j.Log4j2;@Configuration
@EnableWebSecurity
@Log4j2
public class SecurityConfig {@Autowiredprivate WebApplicationContext applicationContext;@Value(value = "${ignore-url}")private String ignoreUrl;@Value(value = "${package.path}")private String packagePath;/** @Description: 配置授權服務器(用于登錄操作)* @author: 胡濤* @mail: hutao_2017@aliyun.com* @date: 2025年5月23日 下午3:15:35*/@Bean@Order(1)public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {OAuth2AuthorizationServerConfigurer authorizationServerConfigurer =OAuth2AuthorizationServerConfigurer.authorizationServer();http.securityMatcher(authorizationServerConfigurer.getEndpointsMatcher()).with(authorizationServerConfigurer, (authorizationServer) ->authorizationServer.oidc(Customizer.withDefaults())).authorizeHttpRequests((authorize) ->authorize.anyRequest().authenticated()).exceptionHandling((exceptions) -> exceptions.defaultAuthenticationEntryPointFor(new LoginUrlAuthenticationEntryPoint("/login"),new MediaTypeRequestMatcher(MediaType.TEXT_HTML)));return http.build();}/** @Description: 配置需要保護的資源* @author: 胡濤* @mail: hutao_2017@aliyun.com* @date: 2025年5月23日 下午2:28:20*/@Bean@Order(2)public SecurityFilterChain resourceServerSecurityFilterChain(HttpSecurity http) throws Exception {//獲取所有的接口地址Map<String, Object> filteredControllers = UrlsUtils.getControllerClass(packagePath, applicationContext);// 使用過濾后的控制器獲取 URLList<String> allUrl = UrlsUtils.getControllerUrls(filteredControllers);String[] split = ignoreUrl.split(";");for (String url : split) {url = url.replaceAll(" ", "");UrlsUtils.removeUrl(allUrl, url);UrlsUtils.removeUrl(allUrl, url);}log.info("受保護的API地址:{}",allUrl);http.securityMatcher(allUrl.toArray(new String[0])).csrf(csrf -> csrf.disable()).authorizeHttpRequests(authorize -> authorize.anyRequest().authenticated()).oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults())).formLogin(form -> form.disable());return http.build();}/** @Description: 配置不需要保護的資源* @author: 胡濤* @mail: hutao_2017@aliyun.com* @date: 2025年5月23日 下午2:28:20*/@Bean@Order(3)public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {String[] split = ignoreUrl.split(";");for (int i = 0; i < split.length; i++) {split[i] = split[i].replaceAll(" ", "");}log.info("受保護的API地址:{}",Arrays.asList(split));http.csrf(csrf -> csrf.ignoringRequestMatchers(split)).authorizeHttpRequests(authorize ->authorize.requestMatchers(split).permitAll().anyRequest().authenticated()).formLogin(Customizer.withDefaults()).logout(Customizer.withDefaults());return http.build();}@Beanpublic UserDetailsService userDetailsService() {UserDetails userDetails = User.withDefaultPasswordEncoder().username("hutao").password("2025.com").roles("USER").build();return new InMemoryUserDetailsManager(userDetails);}@Beanpublic RegisteredClientRepository registeredClientRepository() {RegisteredClient oidcClient = RegisteredClient.withId(UUID.randomUUID().toString()).clientId("oidc-client").clientSecret("{noop}secret").clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC).authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE).authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN).authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS).redirectUri("http://www.baidu.com").postLogoutRedirectUri("http://127.0.0.1:8080/").scope(OidcScopes.OPENID).scope(OidcScopes.PROFILE).clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build()).build();return new InMemoryRegisteredClientRepository(oidcClient);}@Beanpublic JWKSource<SecurityContext> jwkSource() {KeyPair keyPair = generateRsaKey();RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();RSAKey rsaKey = new RSAKey.Builder(publicKey).privateKey(privateKey).keyID(UUID.randomUUID().toString()).build();JWKSet jwkSet = new JWKSet(rsaKey);return new ImmutableJWKSet<>(jwkSet);}private static KeyPair generateRsaKey() {KeyPair keyPair;try {KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");keyPairGenerator.initialize(2048);keyPair = keyPairGenerator.generateKeyPair();}catch (Exception ex) {throw new IllegalStateException(ex);}return keyPair;}@Beanpublic JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);}@Beanpublic AuthorizationServerSettings authorizationServerSettings() {return AuthorizationServerSettings.builder().build();}}

工具類

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;import org.springframework.aop.support.AopUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.WebApplicationContext;/*** @Description:獲取項目的所有接口地址* @author:hutao* @mail:hutao_2017@aliyun.com* @date:2021年5月6日*/
public class UrlsUtils {/** @Description: 獲取controller class* @author: 胡濤* @mail: hutao_2017@aliyun.com* @date: 2025年5月23日 下午4:18:54*/public static Map<String, Object> getControllerClass(String packagePath, WebApplicationContext applicationContext) {Map<String, Object> restControllers = applicationContext.getBeansWithAnnotation(RestController.class);Map<String, Object> controllers = applicationContext.getBeansWithAnnotation(Controller.class);restControllers.putAll(controllers);// 過濾出指定包路徑下的控制器Map<String, Object> filteredControllers = restControllers.entrySet().stream().filter(entry -> {// 處理 CGLIB 代理類(獲取原始類)Class<?> targetClass = AopUtils.getTargetClass(entry.getValue());return targetClass.getPackageName().startsWith(packagePath);}).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));return filteredControllers;}/*** @description:獲取Controller下各方法的接口地址* @author:hutao* @mail:hutao_2017@aliyun.com* @date:2022年3月22日 上午8:54:29*/public static List<String> getControllerUrls(Map<String,Object> controller) {List<String> urls = new ArrayList<>();for (Map.Entry<String, Object> entry : controller.entrySet()) {Object classValue = entry.getValue();Class<?> targetClass = AopUtils.getTargetClass(classValue);String baseUrl = getControllerPath(targetClass);List<Method> declaredMethods = Arrays.asList(targetClass.getDeclaredMethods());for (Method method : declaredMethods) {getMethodPath(urls, baseUrl, method);}}return urls;}/*** @description:移除不需要進行攔截的地址* @author:hutao* @mail:hutao_2017@aliyun.com* @date:2022年3月22日 上午8:54:20*/public static void removeUrl(List<String> urls,String removeUrl) {if(removeUrl.contains("/*")) {Iterator<String> iterator = urls.iterator();while(iterator.hasNext()){  String url = iterator.next(); if(url.contains(removeUrl.replace("*", ""))){  iterator.remove();  }  }  }else {urls.remove(removeUrl);}}/*** @description:獲取所有方法的path* @author:hutao* @mail:hutao_2017@aliyun.com* @date:2022年3月22日 上午8:54:10*/private static void getMethodPath(List<String> urls, String baseUrl, Method method) {String[] mappingValues = null;if(method.getAnnotation(RequestMapping.class) != null) {RequestMapping annotation = method.getAnnotation(RequestMapping.class);mappingValues = annotation.value();}else if (method.getAnnotation(GetMapping.class) != null) {GetMapping annotation = method.getAnnotation(GetMapping.class);mappingValues = annotation.value();}else if (method.getAnnotation(PostMapping.class) != null) {PostMapping annotation = method.getAnnotation(PostMapping.class);mappingValues = annotation.value();}else if (method.getAnnotation(DeleteMapping.class) != null) {DeleteMapping annotation = method.getAnnotation(DeleteMapping.class);mappingValues = annotation.value();}else if (method.getAnnotation(PutMapping.class) != null) {PutMapping annotation = method.getAnnotation(PutMapping.class);mappingValues = annotation.value();}if(mappingValues != null) {for (String mappingValue : mappingValues) {String url = baseUrl+mappingValue;//處理某些controller中requestMapping中沒有添加根路徑if(!url.startsWith("/")) {url = "/"+url;}urls.add(url);}}}/*** @description:獲取controller上面的path地址* @author:hutao* @mail:hutao_2017@aliyun.com* @date:2022年3月22日 上午8:53:59*/private static String getControllerPath(Class<?> targetClass) {String baseUrl = "";RequestMapping controllerMapping = targetClass.getAnnotation(RequestMapping.class);if(controllerMapping != null) {String[] values = controllerMapping.value();if (values.length>0) {baseUrl = values[0];}}return baseUrl;}
}

測試驗證

啟動項目,根據日志,查看相關接口是否需要進行保護
在這里插入圖片描述

SecurityConfig    : 受保護的API地址:[/sysOrg/info/list, /sysOrg/info/test/get, /sysOrg/info/update, 
/sysOrg/info/remove/{id}, /sysOrg/info/save, /sysOrg/info/{id}, /sysOrg/info/list/{id},
/sysOrg/info/page, /sysOrg/info/list/xxxxx]SecurityConfig    : 公共開發的API地址:[/login, /logout, /swagger-ui/**, /v3/api-docs/**, 
/webjars/**, /swagger-resources/**, 
/doc.html, /code/generate, /table/download, /sysUser/**]

以上述日志為例:我公開了:/doc.html, /code/generate, /table/download, /sysUser/**。但sysOrg下的接口,均為開放
公開的API不需要token
在這里插入圖片描述
未公開的API,返回401
在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/82052.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/82052.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/82052.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Python urllib.parse 模塊中的 urljoin 方法

Python urllib.parse 模塊中的 urljoin 方法 urljoin 是 Python 標準庫中 urllib.parse 模塊的一個方法&#xff0c;用于將基礎 URL 和相對路徑拼接成完整的 URL。它會根據傳入的基礎 URL 自動處理協議、域名以及路徑的部分匹配邏輯。 以下是關于該方法的具體說明和示例&…

AI大模型和SpringAI簡介

一、Spring AI 簡介 SpringAI整合了全球&#xff08;主要是國外&#xff09;的大多數大模型&#xff0c;而且對于大模型開發的三種技術架構都有比較好的封裝和支持&#xff0c;開發起來非常方便。 不同的模型能夠接收的輸入類型、輸出類型不一定相同。SpringAI根據模型的輸入…

在TIA 博途中下載程序時找不到對應的網卡怎么辦?

1. 檢查物理連接 確認網線已正確連接PLC和PC&#xff0c;接口指示燈正常。 嘗試更換網線或交換機端口&#xff0c;排除硬件故障。 2. 確認網卡驅動已安裝 設備管理器檢查&#xff1a; 右鍵點擊“此電腦” → “管理” → “設備管理器”。 展開“網絡適配器”&#xff0c;確…

Zabbix實踐!客戶端自動發現

在線答疑&#xff1a;樂維社區 一、客戶端狀態檢查 1.檢查客戶端的zabbix-agent2是否正常 [rootnode1 ~]# systemctl is-active zabbix-agent2.service active 2.從服務端檢查是否可以獲得客戶端信息 [rootIT-01 ~]# zabbix_get -s ‘192.168.200.135’ -p 10050 -k ‘agent.p…

動態規劃中的 求“最長”、“最大收益”、“最多區間”、“最優策略” 雙重 for + 狀態轉移

以最長遞增子序列為例 &#x1f3af; 首先明確目標 以最長上升子序列&#xff08;LIS&#xff09;為例&#xff0c;假設輸入是&#xff1a; nums : []int{10, 9, 2, 5, 3, 7, 101, 18}我們定義&#xff1a; dp[i]&#xff1a;以 nums[i] 為結尾的最長上升子序列長度目標&…

SEO關鍵詞與長尾詞高效布局

內容概要 在SEO優化實踐中&#xff0c;關鍵詞布局的科學性與系統性直接影響流量的獲取效率與可持續性。本文以核心關鍵詞篩選為起點&#xff0c;結合長尾詞挖掘工具與語義關聯分析技術&#xff0c;逐步構建覆蓋用戶全搜索場景的內容矩陣。通過金字塔結構模型&#xff0c;實現高…

考研數一公式筆記

考研數學&#xff08;一&#xff09;核心結論與易錯點詳細筆記 第一部分&#xff1a;高等數學 一、函數、極限、連續 (一) 重要結論與公式 等價無窮小替換 (僅限乘除運算&#xff0c;極限過程為 x → 0 或某特定值導致因子→0)&#xff1a; sin x ~ x tan x ~ x arcsin x …

Debezium TableSchemaBuilder詳解

Debezium TableSchemaBuilder詳解 1. 類的作用與功能 1.1 核心作用 TableSchemaBuilder是Debezium中負責構建表Schema的核心類,主要功能包括: Schema構建:將數據庫表結構轉換為Kafka Connect的Schema定義主鍵處理:生成表的主鍵Schema值Schema處理:生成表的非主鍵字段Sc…

49 python Matplotlib之Pandas 數據可視化

Pandas 是 Python 中用于數據處理的核心庫,其內置了基于 Matplotlib 的可視化功能,可通過 DataFrame.plot() 和 Series.plot() 方法快速生成常見圖表,無需手動編寫繪圖代碼,大幅提升效率。 一、Pandas 核心繪圖方法 基礎語法如下:該代碼為偽代碼,僅做語法說明,無法執行…

《微服務架構設計模式》筆記

思維導圖 1-3章 4-6 章 5-13 章 資料 配套代碼倉庫&#xff1a;https://github.com/microservices-patterns/ftgo-application 作者網站&#xff1a;https://microservices.io/

手寫一個簡單的線程池

手寫一個簡單的線程池 項目倉庫&#xff1a;https://gitee.com/bossDuy/hand-tearing-thread-pool 基于一個b站up的課程&#xff1a;https://www.bilibili.com/video/BV1cJf2YXEw3/?spm_id_from333.788.videopod.sections&vd_source4cda4baec795c32b16ddd661bb9ce865 理…

手機打電話時由對方DTMF響應切換多級IVR語音菜單(完結)

手機打電話時由對方DTMF響應切換多級IVR語音菜單&#xff08;完結&#xff09; --本地AI電話機器人 上一篇&#xff1a;手機打電話時由對方DTMF響應切換多級IVR語音菜單&#xff08;話術腳本與實戰&#xff09; 下一篇&#xff1a;編寫中 一、前言 經過前面幾個篇章的詳細闡…

Android.mk解析

一、變量說明: 1.LOCAL_PATH:= $(call my-dir) 此行代碼在Android.mk的開頭,用于給出當前文件的路徑 LOCAL_PATH 用于在開發樹中查找源文件 宏函數’my-dir’, 由編譯系統提供,用于返回當前路徑(即包含Android.mk file文件的目錄) 2.LOCAL_PACKAGE_NAME := SecSettings …

ip地址改了網絡還能用嗎?ip地址改了有什么后果

當用戶發現自己的網絡出現異常時&#xff0c;常常會疑惑&#xff1a;如果IP地址被更改&#xff0c;網絡是否還能正常使用&#xff1f;要解答這個問題&#xff0c;需要從IP地址的作用、修改方式以及網絡配置等多個角度來分析。 一、IP地址的作用 IP地址是設備在網絡中的唯一標識…

Python-Django系列—日志

Python 程序員通常會在其代碼中使用 print() 作為一種快速和方便的調試工具。使用日志框架只比這多花一點點工夫&#xff0c;但更加優雅和靈活。除了用于調試之外&#xff0c;日志還可以為您提供有關應用程序狀態和健康狀況的更多信息&#xff0c;而且這些信息結構更清晰。 一…

ArcGIS Pro對圖斑進行等比例、等面積、等寬度的分割

ArcGIS全系列實戰視頻教程——9個單一課程組合系列直播回放_arcgis視頻教程我要自學網-CSDN博客 4大遙感軟件&#xff01;遙感影像解譯&#xff01;ArcGISENVIErdaseCognition_遙感解譯軟件-CSDN博客 今天介紹一下ArcGIS Pro對圖斑進行等比例、等面積、等寬度的分割&#xff0…

”故茗”茶文化網站

摘 要 計算機網絡發展到現在已經好幾十年了&#xff0c;在理論上面已經有了很豐富的基礎&#xff0c;并且在現實生活中也到處都在使用&#xff0c;可以說&#xff0c;經過幾十年的發展&#xff0c;互聯網技術已經把地域信息的隔閡給消除了&#xff0c;讓整個世界都可以即時通話…

【和春筍一起學C++】(十五)字符串作為函數參數

1. char指針作為函數參數 在C語言中&#xff0c;表示字符串的方式有3種&#xff1a; char數組用引號括起的字符串常量char指針 這3種形式都可以將其作為實參傳遞給函數中的參數&#xff08;char*&#xff09;&#xff0c;因此函數的形參需要使用char*類型。將字符串作為參數…

VueRouter路由組件的用法介紹

1.1、<router-link>標簽 <router-link>標簽的作用是實現路由之間的跳轉功能&#xff0c;默認情況下&#xff0c;<router-link>標簽是采用超鏈接<a>標簽顯示的&#xff0c;通過to屬性指定需要跳轉的路由地址。當然&#xff0c;如果你不想使用默認的<…

【C/C++】勝者樹與敗者樹:多路歸并排序的利器

文章目錄 勝者樹與敗者樹&#xff1a;多路歸并排序的利器1 勝者樹簡介1.1 定義1.2 勝者樹結構與原理1.2.1 構造流程1.2.2 歸并過程 2 敗者樹簡介2.1 背景場景2.2 基本定義2.3 敗者樹結構和原理2.3.1 樹的構造&#xff08;初始建樹&#xff09;2.3.2 查詢和更新 3 勝者樹 vs 敗者…