電商項目統一認證方案設計與實戰

1.?微服務網關整合?OAuth2.0?設計思路分析

網關整合 OAuth2.0 ?有兩種思路,一種是授權服務器生成令牌, ?所有請求統一?在網關層驗證,判斷權限等操作;另一種是由各資源服務處理,網關只做請求?轉發???比較常用的是第一種,把?API?網關作為?OAuth2.0?的資源服務器角?色,實現接入客戶端權限攔截、令牌解析并轉發當前登錄用戶信息給微服務,?這樣下游微服務就不需要關心令牌格式解析以及?OAuth2.0 相關機制了。

網關在認證授權體系里主要負責兩件事

?1)作為?OAuth2.0?的資源服務器?角色,實現接入方訪問權限攔截。

?(2)令牌解析并轉發當前登錄用戶信息?(明文?token)給微服務

微服務拿到明文?token(明文?token?中包含登錄用戶的?身份和權限信息)后也需要做兩件事

?1)用戶授權攔截(看當前用戶是否有?權訪問該資源?

? (2)將用戶信息存儲進當前線程上下文(有利于后續業務邏?輯隨時獲取當前用戶信息)

?

2.?搭建微服務授權中心

授權中心的認證依賴:

  • ????????第三方客戶端的信息
  • ????????微服務的信息
  • ????????登錄用戶的信息

創建微服務?tulingmall-authcenter

2.1?引入依賴?

<dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId>
</dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId>
</dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId>
</dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId>
</dependency>

2.2?添加 yml 配置?

server:port: 9999
spring:application:name: tulingmall-authcenter#配置nacos注冊中心地址cloud:nacos:discovery:server-addr: 192.168.65.103:8848  #注冊中心地址namespace: 6cd8d896-4d19-4e33-9840-26e4bee9a618  #環境隔離datasource:url: jdbc:mysql://tuling.com:3306/tlmall_oauth?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8username: rootpassword: rootdruid:initial-size: 5 #連接池初始化大小min-idle: 10 #最小空閑連接數max-active: 20 #最大連接數web-stat-filter:exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*" #不統計這些請求數據stat-view-servlet: #訪問監控網頁的登錄用戶名和密碼login-username: druidlogin-password: druid

2.3?配置授權服務器

基于 DB 模式配置授權服務器存儲第三方客戶端的信息

@Configuration
@EnableAuthorizationServer
public class TulingAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {@Autowiredprivate DataSource dataSource;@Overridepublic void configure(ClientDetailsServiceConfigurer clients) throws Exception {// 配置授權服務器存儲第三方客戶端的信息  基于DB存儲   oauth_client_detailsclients.withClientDetails(clientDetails());}@Beanpublic ClientDetailsService clientDetails(){return new JdbcClientDetailsService(dataSource);}}

??oauth_client_details?中添加第三方客戶端信息(client_id????client_secret?????scope?等等)

CREATE TABLE `oauth_client_details`  (`client_id` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,`resource_ids` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`client_secret` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`scope` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`authorized_grant_types` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`web_server_redirect_uri` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`authorities` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`access_token_validity` int(11) NULL DEFAULT NULL,`refresh_token_validity` int(11) NULL DEFAULT NULL,`additional_information` varchar(4096) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`autoapprove` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,PRIMARY KEY (`client_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

?

基于內存模式配置授權服務器存儲第三方客戶端的信息?

//TulingAuthorizationServerConfig.java
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {//  配置授權服務器存儲第三方客戶端的信息  基于DB存儲   oauth_client_details// clients.withClientDetails(clientDetails());/***授權碼模式*http://localhost:9999/oauth/authorize?response_type=code&client_id=client&redirect_uri=http://www.baidu.com&scope=all** password模式*  http://localhost:8080/oauth/token?username=fox&password=123456&grant_type=password&client_id=client&client_secret=123123&scope=all**/clients.inMemory()//配置client_id.withClient("client")//配置client-secret.secret(passwordEncoder.encode("123123"))//配置訪問token的有效期.accessTokenValiditySeconds(3600)//配置刷新token的有效期.refreshTokenValiditySeconds(864000)//配置redirect_uri,用于授權成功后跳轉.redirectUris("http://www.baidu.com")//配置申請的權限范圍.scopes("all")/*** 配置grant_type,表示授權類型* authorization_code: 授權碼* password: 密碼* refresh_token: 更新令牌*/.authorizedGrantTypes("authorization_code","password","refresh_token");}

2.4 ?配置?SpringSecurity

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}@Autowiredprivate TulingUserDetailsService tulingUserDetailsService;@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {// 實現UserDetailsService獲取用戶信息auth.userDetailsService(tulingUserDetailsService);}@Bean@Overridepublic AuthenticationManager authenticationManagerBean() throws Exception {// oauth2 密碼模式需要拿到這個beanreturn super.authenticationManagerBean();}@Overrideprotected void configure(HttpSecurity http) throws Exception {http.formLogin().permitAll().and().authorizeRequests().antMatchers("/oauth/**").permitAll().anyRequest().authenticated().and().logout().permitAll().and().csrf().disable();        }
}

?獲取會員信息?,此處通過 feign?從 tulingmall-member?獲取會員信息?,需要配置?feign?,核心代碼:

@Slf4j
@Component
public class TulingUserDetailsService implements UserDetailsService {@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {// 加載用戶信息if(StringUtils.isEmpty(username)) {log.warn("用戶登陸用戶名為空:{}",username);throw new UsernameNotFoundException("用戶名不能為空");}UmsMember umsMember = getByUsername(username);if(null == umsMember) {log.warn("根據用戶名沒有查詢到對應的用戶信息:{}",username);}log.info("根據用戶名:{}獲取用戶登陸信息:{}",username,umsMember);// 會員信息的封裝 implements UserDetailsMemberDetails memberDetails = new MemberDetails(umsMember);return memberDetails;}@Autowiredprivate UmsMemberFeignService umsMemberFeignService;public UmsMember getByUsername(String username) {// fegin獲取會員信息CommonResult<UmsMember> umsMemberCommonResult = umsMemberFeignService.loadUserByUsername(username);return umsMemberCommonResult.getData();}
}@FeignClient(value = "tulingmall-member",path="/member/center")
public interface UmsMemberFeignService {@RequestMapping("/loadUmsMember")CommonResult<UmsMember> loadUserByUsername(@RequestParam("username") String username);
}public class MemberDetails implements UserDetails {private UmsMember umsMember;public MemberDetails(UmsMember umsMember) {this.umsMember = umsMember;}@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {//返回當前用戶的權限return Arrays.asList(new SimpleGrantedAuthority("TEST"));}@Overridepublic String getPassword() {return umsMember.getPassword();}@Overridepublic String getUsername() {return umsMember.getUsername();}@Overridepublic boolean isAccountNonExpired() {return true;}@Overridepublic boolean isAccountNonLocked() {return true;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return umsMember.getStatus()==1;}public UmsMember getUmsMember() {return umsMember;}
}

修改授權服務配置,支持密碼模式

//TulingAuthorizationServerConfig.java @Autowiredprivate TulingUserDetailsService tulingUserDetailsService;@Autowiredprivate AuthenticationManager authenticationManagerBean;@Overridepublic void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {//使用密碼模式需要配置endpoints.authenticationManager(authenticationManagerBean).reuseRefreshTokens(false)  //refresh_token是否重復使用.userDetailsService(tulingUserDetailsService) //刷新令牌授權包含對用戶信息的檢查.allowedTokenEndpointRequestMethods(HttpMethod.GET,HttpMethod.POST); //支持GET,POST請求}/*** 授權服務器安全配置* @param security* @throws Exception*/@Overridepublic void configure(AuthorizationServerSecurityConfigurer security) throws Exception {//第三方客戶端校驗token需要帶入 clientId 和clientSecret來校驗security.checkTokenAccess("isAuthenticated()").tokenKeyAccess("isAuthenticated()");//來獲取我們的tokenKey需要帶入clientId,clientSecret//允許表單認證security.allowFormAuthenticationForClients();}

?2.5?測試模擬用戶登錄

授權碼模式

授權碼(authorization?code)方式,指的是第三方應用先申請一個授權碼,然?后再用該碼獲取令牌。

這種方式是最常用的流程,安全性也最高,它適用于那些有后端的 Web???用。授權碼通過前端傳送,令牌則是儲存在后端,而且所有與資源服務器的通?信都在后端完成。這樣的前后端分離,可以避免令牌泄漏。

適用場景:?目前市面上主流的第三方驗證都是采用這種模式

它的步驟如下:

A)用戶訪問客戶端,后者將前者導向授權服務器。

B)用戶選擇是否給予客戶端授權。

(C)假設用戶給予授權,授權服務器將用戶導向客戶端事先指定的"重定向URI"( redirection URI),同時附上一個授權碼。

D)客戶端收到授權碼,附上早先的"重定向?URI"?,向授權服務器申請令 牌。這一步是在客戶端的后臺的服務器上完成的,對用戶不可見。

E)授權服務器核對了授權碼和重定向?URI?,確認無誤后,向客戶端發送?訪問令牌(access token)和更新令牌(?refresh token)。

http://localhost:9999/oauth/authorize?response_type=code&client_id=client?&redirect_uri=http://www.baidu.com&scope=all

獲取到?code

?

密碼模式

如果你高度信任某個應用,RFC?6749 ?也允許用戶把用戶名和密碼,直接告訴該??????用就?使?用你????????????????" ???"?password)。

在這種模式中,用戶必須把自己的密碼給客戶端,但是客戶端不得儲存密碼。?這通常用在用戶對客戶端高度信任的情況下,?比如客戶端是操作系統的一部?分,或者由一個著名公司出品。而授權服務器只有在其他授權模式無法執行的?情況下,才能考慮使用這種模式。

適用場景:?自家公司搭建的授權服務器

測試獲取?token

http://localhost:9999/oauth/token?username=test&password=test&grant_type=password&client_id=clien?t&client_secret=?123123&scope=all

測試校驗?token?接口?

?因為授權服務器的?security?配置需要攜帶?clientId??clientSecret?,可以采用?basic?Auth??的方?式發請求

?注意:?傳參是?token

2.6?配置資源服務器

@Configuration
@EnableResourceServer
public class TulingResourceServerConfig  extends ResourceServerConfigurerAdapter {@Overridepublic void configure(HttpSecurity http) throws Exception {http.authorizeRequests().anyRequest().authenticated();}
}@RestController
@RequestMapping("/user")
public class UserController {@RequestMapping("/getCurrentUser")public Object getCurrentUser(Authentication authentication) {return authentication.getPrincipal();}
}

測試攜帶token?訪問資源

或者請求頭配置?Authorization

OAuth?2.0?是當前業界標準的授權協議,它的核心是若干個針對不同場景的令牌頒發和管?理流程;而?JWT?是一種輕量級、?自包含的令牌,可用于在微服務間安全地傳遞用戶信息。

2.7 Spring Security Oauth2 整合 JWT

JSON Web TokenJWT)是一個開放的行業標準(RFC?7519),它定義了一種簡介的、?自包含的協議格式,用于在通信雙方傳遞?json?對象,傳遞的信息經過數?字簽名可以被驗證和信任。JWT?可以使用?HMAC?算法或使用?RSA?公鑰/私鑰對?來簽名,防止被篡改。 官網:JSON Web Tokens - jwt.io

JWT?令牌的優點:

  • ????????jwt 基于?json?,非常方便解析。
    • ????????可以在令牌中自定義豐富的內容,易擴展。
      • ????????通過非對稱加密算法及數字簽名技術,JWT?防止篡改,安全性高。
        • ????????資源服務使用JWT?可不依賴認證服務即可完成授權。

缺點:

????????JWT?令牌較長,?占存儲空間比較大。

JWT:指的是 JSON?Web?Token???header.payload.signture???組成。不存在簽名的?JWT??不安全的,存在簽名的?JWT?是不可竄改的。

JWS:指的是簽過名的?JWT?,即擁有簽名的?JWT

JWK:既然涉及到簽名,就涉及到簽名算法,對稱加密還是非對稱加密,那么就需要加密?的 密鑰或者公私鑰對。此處我們將 JWT??的密鑰或者公私鑰對統一稱為 JSON?WEB?KEY?,即?JWK

JWT?組成

一個?JWT?實際上就是一個字符串,它由三部分組成,頭部(header?、載荷?payload)與簽名(signature)。

頭部(header

頭部用于描述關于該?JWT?的最基本的信息:類型(即?JWT)以及簽名所用的?算法(如?HMACSHA256 ?RSA)等。

這也可以被表示成一個?JSON 對象:

{"alg": "HS256","typ": "JWT"
}

?然后將頭部進行?base64 加密(該加密是可以對稱解密的),構成了第一部分:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9?

載荷(payload

第二部分是載荷,就是存放有效信息的地方。這個名字像是特指飛機上承載的?貨品,這些有效信息包含三個部分:

  • 標準中注冊的聲明(建議但不強制使用)

????????iss: jwt 簽發者

????????sub: jwt 所面向的用戶

????????aud:?接收?jwt?的一方

????????exp:?jwt?的過期時間,這個過期時間必須要大于簽發時間

????????nbf: ?定義在什么時間之前,該?jwt 都是不可用的.

????????iat: jwt?的簽發時間

????????jti:?jwt?的唯一身份標識,主要用來作為一次性?token,從而回避重放攻擊。

  • 公共的聲明

????????公共的聲明可以添加任何的信息,一般添加用戶的相關信息或?其他業務需要的必要信息.但不建議添加敏感信息,因為該部分在客戶端可解密.

  • 私有的聲明

????????私有聲明是提供者和消費者所共同定義的聲明,一般不建議存?放敏感信息,因為?base64 是對稱解密的,意味著該部分信息可以歸類為明文?信息。

定義一個?payload

{"sub": "1234567890","name": "John Doe","iat": 1516239022
}

?然后將其進行?base64 加密,得到?Jwt?的第二部分:

eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ???????

簽名(signature

jwt?的第三部分是一個簽證信息,這個簽證信息由三部分組成:

  • header (base64 后的)
  • payload (base64 后的)
  • secret(鹽,一定要保密)

這個部分需要?base64 加密后的?header ?base64 加密后的?payload?使用.連接?組成的字符串,然后通過?header?中聲明的加密方式進行加鹽?secret?組合加?密,然后就構成了?jwt?的第三部分:

var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);
var signature = HMACSHA256(encodedString, 'fox'); // khA7TNYc7_0iELcDyTc7gHBZ_xfIcgbfpzUNWwQtzME

?將這三部分用.連接成一個完整的字符串,構成了最終的?jwt:

?eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.khA7TNYc7_0iELcDyTc7gHBZ_xfIcgbfpzUNWwQtzME

注意:secret?是保存在服務器端的,jwt?的簽發生成也是在服務器端的,secret 就是用來進行?jwt?的簽發和?jwt?的驗證,所以,它就是你服務端的私鑰,在任何?場景都不應該流露出去。一旦客戶端得知這個?secret, ?那就意味著客戶端是可以 自我簽發?jwt?了。

JWT 應用場景???????

  • 一次性驗證

????????比如用戶注冊后需要發一封郵件讓其激活賬戶,通常郵件中需要有一個鏈接,這個鏈接需?要具備以下的特性:能夠標識用戶,該鏈接具有時效性〈(通常只允許幾小時之內激活)?,不?能被篡改以激活其他可能的賬戶這種場景就和?jwt?的特性非常貼近,jwt?payload???中固定?的參數:?iss?簽發者和?exp?過期時間正是為其做準備的。

  • restful?api?的無狀態認證

????????使用?jwt?來做?restful?api?的身份認證也是值得推崇的一種使用方案。客戶端和服務端共享?secret;過期時間由服務端校驗,客戶端定時刷新;簽名信息不可被修改。

  • 使用?jwt?做單點登錄+會話管理(不推薦) ??????????token+redis

????????jwt?是無狀態的,在處理注銷,續約問題上會變得非常復雜

引入依賴

<!--spring secuity對jwt的支持 spring cloud oauth2已經依賴,可以不配置-->
<dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-jwt</artifactId><version>1.0.9.RELEASE</version>
</dependency>

?添加?JWT?配置

@Configuration
public class JwtTokenStoreConfig {@Beanpublic TokenStore jwtTokenStore(){return new JwtTokenStore(jwtAccessTokenConverter());}@Beanpublic JwtAccessTokenConverter jwtAccessTokenConverter(){JwtAccessTokenConverter accessTokenConverter = newJwtAccessTokenConverter();//配置JWT使用的秘鑰accessTokenConverter.setSigningKey("123123");return accessTokenConverter;}
}

?在授權服務器配置中指定令牌的存儲策略為?JWT

//TulingAuthorizationServerConfig.java@Autowired
@Qualifier("jwtTokenStore")
private TokenStore tokenStore;@Autowired
private JwtAccessTokenConverter jwtAccessTokenConverter;@Autowired
private TulingUserDetailsService tulingUserDetailsService;@Autowired
private AuthenticationManager authenticationManagerBean;@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {//使用密碼模式需要配置endpoints.authenticationManager(authenticationManagerBean).tokenStore(tokenStore)  //指定token存儲策略是jwt.accessTokenConverter(jwtAccessTokenConverter).reuseRefreshTokens(false)  //refresh_token是否重復使用.userDetailsService(tulingUserDetailsService) //刷新令牌授權包含對用戶信息的檢查.allowedTokenEndpointRequestMethods(HttpMethod.GET,HttpMethod.POST); //支持GET,POST請求
}

密碼模式測試:

http://localhost:9999/oauth/token?username=test&password=test&grant_type=password&client_id=clien?t&client_secret=123123&scope=all

?access_token?復制到?JSON Web Tokens - jwt.io?Encoded?中打開,可以看到會員認證信息

?測試校驗?token

測試獲取?token_key?

?

測試刷新?token

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

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

相關文章

學習Markdown

標題一 標題二 標題三 標題四 標題五 標題六這是一段引用文本直接編寫&#xff0c;段落換行是末尾兩個以上的空格&#xff0b;回車 或者在段落后加一個空行 粗體語法&#xff1a;使用兩個星號 ** 或兩個下劃線 __ 包圍文字&#xff1a;這是粗體文字使用星號 這是__粗體文字__使…

劇本殺系統 App 開發:科技賦能,重塑劇本殺游戲體驗

在科技飛速發展的當下&#xff0c;各個行業都在積極尋求與科技的融合&#xff0c;以實現創新和升級。劇本殺行業也不例外&#xff0c;劇本殺系統 App 的開發正是科技賦能的生動體現&#xff0c;它重塑了傳統的劇本殺游戲體驗&#xff0c;為玩家帶來了全新的感受。劇本殺系統 Ap…

wvp-gb28181-pro 只用jar運行

編譯前端后npm install --global yarnyarn --registryhttps://registry.npmjs.org installyarn run build&#xff0c;生成的前端文件&#xff0c;會在wvp-GB28181-pro\src\main\resources\static&#xff0c;因為是在resources中&#xff0c;打maven打包后會一起打到jar中&…

深度學習(魚書)day06--神經網絡的學習(后兩節)

深度學習&#xff08;魚書&#xff09;day06–神經網絡的學習&#xff08;后兩節&#xff09;一、梯度 像 這樣的由全部變量的偏導數匯總而成的向量稱為梯度&#xff08;gradient&#xff09;。 梯度實現的代碼&#xff1a; def numerical_gradient(f, x):h 1e-4 # 0.0001grad…

學習嵌入式的第三十四天-數據結構-(2025.7.29)數據庫

數據庫基礎概念 數據庫是用于存儲和管理海量數據的應用程序&#xff0c;提供數據增刪改查及統計功能&#xff08;如最大值、最小值、平均數等&#xff09;。通過SQL語句操作數據&#xff0c;以表格形式管理存儲。 數據庫分類 關系型數據庫 Oracle&#xff08;大型&#xff0…

STM32——HAL庫

總&#xff1a;STM32——學習總綱 一、簡介 1.1 CMIS簡介 所有廠家為了市場兼容性推出的標準 arm架構 1.2 HAL庫簡介 1.2.1 各種庫優缺點 二、 STM32 Cube固件包 ST公司為CMSIS 中間層開發的pack&#xff0c;包含HAL。 2.1 獲取方式 ST官網&#xff1a;st.com/content/st_c…

數據結構-圖的相關定義

圖-多對多Graph&#xff08;V,E&#xff09;&#xff0c;圖&#xff08;頂點Vertex&#xff0c;邊Edge&#xff09;圖可以沒有邊&#xff0c;只有一個頂點也叫圖&#xff0c;但是單獨的一條邊&#xff0c;或者一個頂點連一條邊&#xff0c;不能叫圖有向圖&#xff1a;無向圖&am…

B 站搜一搜關鍵詞優化:精準觸達用戶的流量密碼

在 B 站內容生態中&#xff0c;搜一搜功能是用戶主動獲取信息的重要渠道&#xff0c;而關鍵詞優化則是讓你的視頻在搜索結果中脫穎而出的關鍵。通過合理優化關鍵詞&#xff0c;能提升視頻曝光率&#xff0c;吸引精準流量&#xff0c;為賬號發展注入強勁動力。以下從關鍵詞挖掘、…

Python爬蟲實戰:研究purl庫相關技術

1. 引言 隨著互聯網數據量的爆炸式增長,網絡爬蟲已成為數據采集、輿情分析和學術研究的重要工具。Python 憑借其豐富的庫生態和簡潔語法,成為開發爬蟲的首選語言。本文提出的爬蟲系統結合 requests 進行 HTTP 請求、BeautifulSoup 解析 HTML,并創新性地引入 purl 庫處理復雜…

OpenCV 學習探秘之三:從圖像讀取到特征識別,再到機器學習等函數接口的全面實戰應用與解析

一、引言 1.1介紹 OpenCV&#xff08;Open Source Computer Vision Library&#xff09;是一個功能強大的開源計算機視覺庫&#xff0c;廣泛應用于圖像和視頻處理、目標檢測、機器學習等領域。本文將全面解析 OpenCV 中常用的函數接口&#xff0c;幫助讀者快速掌握 OpenCV 的…

Umi從零搭建Ant Design Pro項目(3)集成 openapi 插件

1. 安裝插件 pnpm add umijs/max-plugin-openapi pnpm add swagger-ui-dist如果不安裝swagger-ui-dist&#xff0c;不會影響運行。但會報錯。 2.配置文件export default defineConfig({// umi插件配置plugins: [umijs/max-plugin-openapi],// openAPI配置openAPI: {requestLibP…

Flutter開發實戰之狀態管理深入解析

第4章:狀態管理深入解析 前言 想象一下,你正在開發一個購物車應用。用戶在商品頁面添加商品,然后去購物車頁面查看,最后到結算頁面付款。在這個過程中,購物車的數據需要在多個頁面之間保持同步和一致。這就是狀態管理要解決的核心問題。 狀態管理是Flutter開發中最重要…

組件化(一):重新思考“組件”:狀態、視圖和邏輯的“最佳”分離實踐

組件化(一)&#xff1a;重新思考“組件”&#xff1a;狀態、視圖和邏輯的“最佳”分離實踐 引子&#xff1a;組件的“內憂”與“外患” 至此&#xff0c;我們的前端內功修煉之旅已經碩果累累。我們掌握了組件化的架構思想&#xff0c;擁有了高效的渲染引擎&#xff0c;還探索…

【Redis】Redis 協議與連接

一、Redis 協議 1.1 RESP RESP 是 Redis 客戶端與服務器之間的通信協議&#xff0c;采用文本格式&#xff08;基于 ASCII 字符&#xff09;&#xff0c;支持多種數據類型的序列化和反序列化 RESP 通過首字符區分數據類型&#xff0c;主要支持 5 種類型&#xff1a; 類型首字…

Android通知(Notification)全面解析:從基礎到高級應用

一、Android通知概述通知(Notification)是Android系統中用于在應用之外向用戶傳遞信息的重要機制。當應用需要告知用戶某些事件或信息時&#xff0c;可以通過通知在狀態欄顯示圖標&#xff0c;用戶下拉通知欄即可查看詳細信息。這種機制幾乎被所有現代應用采用&#xff0c;用于…

VUE3(四)、組件通信

1、props作用&#xff1a;子組件之間的通信。父傳子&#xff1a;屬性值的非函數。子傳父&#xff1a;屬性值是函數。父組件&#xff1a;<template><div>{{ childeData }}</div>——————————————————————————————<child :pare…

【數據結構與算法】數據結構初階:詳解二叉樹(六)——二叉樹應用:二叉樹選擇題

&#x1f525;個人主頁&#xff1a;艾莉絲努力練劍 ?專欄傳送門&#xff1a;《C語言》、《數據結構與算法》、C語言刷題12天IO強訓、LeetCode代碼強化刷題 &#x1f349;學習方向&#xff1a;C/C方向 ??人生格言&#xff1a;為天地立心&#xff0c;為生民立命&#xff0c;為…

Android廣播實驗

【實驗目的】了解使用Intent進行組件通信的原理&#xff1b;了解Intent過濾器的原理和匹配機制&#xff1b;掌握發送和接收廣播的方法【實驗內容】任務1、普通廣播&#xff1b;任務2、系統廣播&#xff1b;任務3、有序廣播&#xff1b;【實驗要求】1、練習使用靜態方法和動態方…

html轉word下載

一、插件使用//轉html為wordnpm i html-docx-js //保存文件到本地npm i file-saver 注&#xff1a;vite 項目使用esm模式會報錯&#xff0c;with方法錯誤&#xff0c;修改如下&#xff1a;//直接安裝修復版本npm i html-docx-fixed二、封裝導出 exportWord.jsimport htmlDocx f…

北方公司面試記錄

避免被開盒&#xff0c;先稱之為“北方公司”&#xff0c;有確定結果后再更名。 先說流程&#xff0c;線下面試&#xff0c;時間非常急&#xff0c;下午兩點鐘面試&#xff0c;中午十二點打電話讓我去&#xff0c;帶兩份紙質簡歷。 和一般的菌工單位一樣&#xff0c;先在傳達室…