SpringBoot后端開發常用工具詳細介紹——SpringSecurity認證用戶保證安全

簡單的開始

創建SpringBoot項目

首先創建一個簡單的springboot項目,假設端口為8888,添加controller控制層,并在其中添加TestController控制類,那么啟動springboot項目之后,訪localhost:8888/api/message頁面會顯示my first message

@RestController
@RequestMapping("/api")
public TestController{@GetMapping("/messages")public String myMessage(){return "my first message";}
}

添加SpringSecurity的依賴

<dependencies><!-- ... 其他依賴元素 ... --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId><!--可以通過下面的內容進行版本指定--><spring-security.version>6.2.0-SNAPSHOT</spring-security.version></dependency>
</dependencies>

SpringSecurity認證登錄

運行springboot項目后,在控制臺輸出窗口出現:

Using generated security password: 8e557245-73e2-4286-969a-ff57fe326336

嘗試訪問localhost:port任意后端接口地址,可以發現出現了登錄窗口,

使用user: user password:8e557245-73e2-4286-969a-ff57fe326336 這里的密碼就是控制臺輸出的密碼。

這就是springsecurity的端口認證機制。

原理說明

Filter和FilterChain

當客戶端向應用程序發送請求時,SpringSecurity會創建一系列的Filter來過濾請求,這樣的Filter有多個,這些Filter構成了從客戶端到Servlet的一個FilterChain,在通過FilterChain的過濾之后,這個請求才會被Servlet處理。

需要注意的是Filter 會影響下游的 Filter 實例,當匹配到一個Filter之后就不再匹配下面的Filter

流程如下所示。

filterchain

過濾過程的偽代碼

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {// 在過濾之前的動作chain.doFilter(request, response); // 進行過濾// 過濾之后的動作
}

FilterChainProxy和SecurityFilterChain

基本流程

當一個請求來臨時,我們常常會有這樣的動作,對于某一個請求接口,去查看對應的過濾器,比如對account相關的接口,我們就會給account制定對應的過濾器,當account相關的請求來臨時,我們必然的就需要去通過account的過濾器去處理該請求。

FilterChainProxy就是這樣的一個角色, 用來確定當前請求應該調用哪些 Spring Security Filter 實例。

SecurityFilterChain的作用就是將過濾器進行分類,用來被FilterChainProxy識別調用。

securityfilterchain

因此當設計多個接口過濾器時,基本架構如下圖所示

multi securityfilterchain

舉例說明

FilterChainProxy 決定應該使用哪個 SecurityFilterChain。只有第一個匹配的 SecurityFilterChain 被調用。

  • 如果請求的URL是 /api/messages,它首先與 /api/**SecurityFilterChain0 模式匹配,所以只有 SecurityFilterChain0 被調用,盡管它也與 SecurityFilterChainn 匹配。
  • 如果請求的URL是 /messages,它與 /api/**SecurityFilterChain_0 模式不匹配,所以 FilterChainProxy 會繼續順序嘗試下面的 SecurityFilterChain。假設沒有其他 SecurityFilterChain 實例相匹配,則調用 SecurityFilterChain_n

工作流程

客戶端請求受保護資源
Spring Security 過濾器鏈攔截請求
UserDetailsServiceAutoConfiguration 配置 AuthenticationManager
需要認證?
UsernamePasswordAuthenticationFilter 攔截
訪問資源
DaoAuthenticationProvider 處理認證
使用自定義 UserDetailsService?
自定義 UserDetailsService 加載用戶信息
InMemoryUserDetailsManager 加載默認用戶信息
密碼匹配?
創建 Authentication 對象
拋出 AuthenticationException
將 Authentication 對象放入 SecurityContextHolder
訪問資源
重定向到登錄頁面或返回錯誤

1. 自動配置的過程

  1. UserDetailsServiceAutoConfiguration 類上的條件注解

    • @ConditionalOnClass(AuthenticationManager.class)

      ? 確保 AuthenticationManager 類在類路徑上。

    • @ConditionalOnBean(ObjectPostProcessor.class)

      ? 確保 Spring 容器中存在 ObjectPostProcessor 的 Bean

    • @ConditionalOnMissingBean({ AuthenticationManager.class, AuthenticationProvider.class, UserDetailsService.class })

      ? 確保 Spring 容器中沒有定義 AuthenticationManagerAuthenticationProviderUserDetailsService 的 Bean。

  2. 默認InMemoryUserDetailsManager 的Bean 的創建

    如果上述條件都滿足,UserDetailsServiceAutoConfiguration 會創建一個 InMemoryUserDetailsManager 的 Bean 作為默認的用戶詳細信息服務管理器。這個管理器會在內存中創建一個用戶,通常用戶名為 “user”,密碼為隨機生成的 UUID,這個角色為 “USER”。

    在創建 InMemoryUserDetailsManager 時,UserDetailsServiceAutoConfiguration 會檢查 SecurityProperties 中定義的用戶密碼。如果密碼是生成的,它會記錄一條日志,顯示使用的密碼。同時,它還會檢查密碼是否已經使用某種算法進行了編碼,如果沒有,它會使用 {noop} 前綴,表示密碼沒有被編碼。

    創建過程代碼:可以簡單瀏覽,之后自己創建配置時會借鑒到

        @Beanpublic InMemoryUserDetailsManager inMemoryUserDetailsManager(SecurityProperties properties,// springsecurity的配置文件,里面有默認的用戶名,可以進入 SecurityProperties 查看詳細數據ObjectProvider<PasswordEncoder> passwordEncoder) // 密碼編碼器{SecurityProperties.User user = properties.getUser(); // 配置文件中的用戶List<String> roles = user.getRoles(); // 獲取角色return new InMemoryUserDetailsManager(new UserDetails[]{User.withUsername(user.getName()) // 賬號.password(		// 密碼this.getOrDeducePassword(user, (PasswordEncoder)passwordEncoder.getIfAvailable())).roles(StringUtils.toStringArray(roles)) //角色.build()});}
  3. 注冊Bean

    最終,UserDetailsServiceAutoConfiguration 會將 InMemoryUserDetailsManager 注冊為 Spring 應用上下文中的一個 Bean,這樣 Spring Security 在認證時就可以使用這個默認的用戶詳細信息服務。

2. UserDetailsService的作用

我們通過上面InMemoryUserDetailsManager的類,可以分析得出

  • InMemoryUserDetailsManager實現了UserDetailsManager、UserDetailsPasswordService中的方法

  • UserDetailsManager繼承UserDetailsService

    在這里插入圖片描述

因此InMemoryUserDetailsManager的關鍵就是UserDetailsManagerUserDetailsPasswordService以及實現自UserDetailsService中的方法

接口方法描述
UserDetailsManagervoid createUser(UserDetails user)根據提供的用戶詳情創建一個新用戶賬號
void updateUser(UserDetails user)更新指定的用戶賬號
void deleteUser(String username)從系統中刪除具有給定登錄名的用戶賬號
void changePassword(String oldPassword, String newPassword)修改用戶賬號的密碼。這應該在持久的用戶存儲庫中更改用戶的密碼(數據庫、LDAP等)
boolean userExists(String username)檢查具有給定登錄名的用戶賬號是否存在于系統中
UserDetails loadUserByUsername(String username)根據用戶名加載用戶信息,此方法從 UserDetailsService 繼承
UserDetailsPasswordServiceUserDetails updatePassword(UserDetails user, String newPassword)更新用戶密碼。在用戶登錄成功后,如果檢測到密碼需要更新(例如,密碼策略變更),則調用此方法

? 而我們可以通過上面部分自動配置過程可以知道,假如Spring 容器中定義了 AuthenticationManagerAuthenticationProviderUserDetailsService 的 Bean,那么自動配置文件將不會生效。

3. AuthenticationManager的作用

在Spring Security中,AuthenticationManager 是一個核心接口,負責對用戶的認證請求進行處理。它定義了一個 authenticate 方法,該方法接受一個 Authentication 對象作為參數,并返回一個完全認證過的 Authentication 對象。如果認證失敗,則拋出 AuthenticationException

ProviderManagerAuthenticationManager 的一個常見實現,它使用一個 AuthenticationProvider 列表來處理認證請求。每個 AuthenticationProvider 都有機會對認證請求進行處理,如果一個 AuthenticationProvider 無法處理請求,ProviderManager 會嘗試下一個。這個過程會一直持續,直到找到一個能夠成功認證請求的 AuthenticationProvider,或者所有的 AuthenticationProvider 都嘗試完畢。

找到匹配的 Provider
認證成功
認證失敗
未找到匹配的 Provider
開始認證
AuthenticationManager
ProviderManager
遍歷 AuthenticationProvider 列表
Provider 進行認證
返回認證后的 Authentication 對象
拋出 AuthenticationException
拋出 ProviderNotFoundException
認證完成

4. 手動配置賬號密碼

1)創建配置類、用戶管理器

因此我們創建自己的WebSecurityConfig類 ,在里面進行InMemoryUserDetailsManager的注入,并實現構造方法。這樣我們就手動創建了自己的配置內容。

@Configuration
public class WebSecurityConfig {@Beanpublic InMemoryUserDetailsManager inMemoryUserDetailsManager() {return new InMemoryUserDetailsManager();}
}

當然我們里面還沒有給InMemoryUserDetailsManager添加任何用戶。

2)初始化用戶

添加下面代碼,在創建InMemoryUserDetailsManager時新建一個用戶

@Configuration
public class WebSecurityConfig {@Beanpublic InMemoryUserDetailsManager inMemoryUserDetailsManager() {return new InMemoryUserDetailsManager(User.withUsername("user") // 用戶名.password("{noop}password") // 密碼,以{noop}開頭的話代表不加密.roles("a") // 使用可變參數傳遞角色.build());}
}

這樣,當我們啟動時,就可以根據上面的賬號和密碼進行登錄

3)添加用戶

當然我們也可以通過調用InMemoryUserDetailsManager中的createUser方法添加用戶的方式,來初始化manager用戶管理器,下面我們展示創建兩個用戶的過程。

@Configuration
public class WebSecurityConfig {@Beanpublic InMemoryUserDetailsManager inMemoryUserDetailsManager() {InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();manager.createUser(new User("admin", "{noop}123456", List.of(new SimpleGrantedAuthority("ROLE_ADMIN"))));manager.createUser(new User("user", "{noop}654321", List.of(new SimpleGrantedAuthority("ROLE_USER"))));return manager;}
}
4)認證過程

我們通過上面的內容已經知道了初始化用戶 添加用戶,同樣的里面的updateUser deleteUser changePassword userExists方法也基本類似,不再贅述。

接下來需要弄懂的就是如何認證的呢,我們明明沒有寫這些相關的方法。

通過最開始的流程圖,我們可以知道在配置好認證用戶之后,之后程序對于每一個請求都會進行攔截。

外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳

請求攔截

AbstractAuthenticationProcessingFilter將請求攔截,并通過調用attemptAuthentication方法進行處理,而這個方法的具體實現存在于UsernamePasswordAuthenticationFilter

將請求進行攔截,然后交給授權管理器AuthenticationManager進行控制
在這里插入圖片描述

授權管理器認證

進入authenticate()方法發現進入到一個AuthenticationManager接口中,而這個接口的實現類是ProviderManager

外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳

ProviderManager類中的authenticate方法委派認證工作給一個或多個AuthenticationProvider

驗證用戶是否存在

AuthenticationProvider仍為一個接口,其默認實現類為AbstractUserDetailsAuthenticationProvider

AbstractUserDetailsAuthenticationProviderauthenticate方法流程如下:

  • 開始認證:認證過程開始。
  • 檢查Authentication類型:確保傳入的Authentication對象是UsernamePasswordAuthenticationToken類型。
  • 拋出異常:如果類型不匹配,拋出異常。
  • 確定用戶名:從Authentication對象中獲取用戶名。
  • 從Cahce緩存獲取UserDetails:嘗試從Cahce緩存中獲取UserDetails對象。
  • Cahce未命中:如果Cahce未命中,從用戶信息源(如數據庫)檢索用戶信息。
  • 用戶不存在:如果用戶不存在,根據配置拋出UsernameNotFoundExceptionBadCredentialsException
  • 用戶存在:如果用戶存在,校驗用戶狀態(如賬戶是否過期、是否鎖定等)。
  • 用戶狀態無效:如果用戶狀態無效,拋出AuthenticationException
  • 執行額外的認證檢查:執行任何額外的認證檢查(如密碼過期檢查)。
  • 認證檢查失敗:如果認證檢查失敗,重新檢索用戶信息并再次執行檢查。
  • 執行后置認證檢查:執行認證成功后的后置檢查。
  • 后置檢查失敗:如果后置檢查失敗,拋出AuthenticationException
  • 檢查是否使用緩存:檢查認證過程中是否使用了緩存。
  • 使用了緩存:如果沒有使用緩存,將用戶信息放入緩存。
  • 創建認證成功的Authentication對象:創建一個新的Authentication對象,表示認證成功。
  • 返回認證成功的Authentication對象:返回認證成功的Authentication對象。

先看前半部分查看用戶是否存在

在這里調用了retrieveUser方法來進行用戶驗證獲取驗證結果,這個方法在DaoAuthenticationProvider中進行驗證,是否存在該用戶。
在這里插入圖片描述

DaoAuthenticationProvider中調用loadUserByUsername方法進行具體內容的驗證,這個方法在前面UserDetailsService的作用中看到過
在這里插入圖片描述

驗證密碼是否正確

在完成用戶存在驗證后,我們繼續看AbstractUserDetailsAuthenticationProvider類,在這個類中使用additionalAuthenticationChecks方法進行賬號密碼的驗證。
在這里插入圖片描述

具體內容的實現仍在在DaoAuthenticationProvider
在這里插入圖片描述

5)請求攔截

上面我們可以知道UsernamePasswordAuthenticationFilter攔截器,攔截的只是login的請求,那對于之后的每一次請求是個什么樣的流程呢

通過攔截每一次請求,接著驗證是否被授權,因此我們之后在處理請求攔截時,可以同樣采用這樣的方式,進行借鑒
在這里插入圖片描述

6)匯總
發送登錄請求
檢查請求
默認
成功
失敗
客戶端
DispatcherServlet
Spring Security Filter Chain
SecurityContextHolder
UsernamePasswordAuthenticationFilter
請求路徑和方法匹配?
提取用戶名和密碼
繼續過濾器鏈
創建 UsernamePasswordAuthenticationToken
調用 AuthenticationManager
AuthenticationManager
委派給 AuthenticationProvider
DaoAuthenticationProvider
調用 UserDetailsService
UserDetailsService
加載 UserDetails
密碼驗證
返回 Authentication 對象
拋出 AuthenticationException
設置 SecurityContextHolder
認證成功處理
AuthenticationSuccessHandler
繼續過濾器鏈
AuthenticationFailureHandler
重定向或返回成功響應
重定向到登錄頁面或顯示錯誤
客戶端
7)關于加密的過程

很多配置都是通過大致流程,因此可以擴展到理解其他的一些配置項。

我們發現在上面密碼驗證時,是設置了編碼器,那我們從來沒有配置過DaoAuthenticationProvider,這里的密碼加密器是怎么配置的呢?

DaoAuthenticationProvider構造方法設置加密器的位置添加斷點。然后執行程序時不斷進入斷點。
在這里插入圖片描述
進入了InitializeUserDetailsBeanManagerConfigurer
在這里插入圖片描述

5. 結合數據庫進行用戶認證

數據庫和上面配置過程不同的是:

手動配置

  • 首先創建springSecurity的用戶
  • 在登錄時對用戶進行認證
  • 與前面創建的用戶進行匹配

數據庫配置

  • 不需要創建用戶
  • 登錄時直接與數據庫中的用戶進行匹配

經過上面的過程,我們可以知道,主要的過程就是DaoAuthenticationProvider創建時設置的UserDetailsService,可以控制用戶的認證。

1)引入數據庫

我們采用springdatajpa操作數據庫

向pom中添加

<dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><scope>runtime</scope>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

設置yml內容

spring:datasource:url: jdbc:mysql://localhost:3306/springsecurityusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driverjpa:hibernate:ddl-auto: update #自動生成數據庫show-sql: true
1)創建實體類

創建好之后記得手動在數據庫中添加一條數據用于測試

@Data
@Entity
@Table(name = "sys_user")
public class User {@Column(name = "user_id", unique = true, nullable = false, insertable = false, updatable = false)@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private int userId;@Column(name = "mobile")private String mobile;@Column(name = "pwd")private String password;@Column(name="identity")private int identity;@Column(name="nick_name")private String nickName;
}
2)創建Dao層
@Repository
public interface UserDao extends JpaRepository<User,Integer> {User findByMobileAndPassword(String mobile,String pwd);User findByMobile(String mobile);
}
3)仿照InMemoryUserDetailsManager創建MyUserDetailsManager

我們上面知道了,要想控制賬號密碼的驗證,我們就需要自己注入UserDetailsService,這樣他就不會采用系統本身的驗證方案了。

@Component
public class MyUserDetailsManager implements UserDetailsService {@Resourceprivate UserDao userDao;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {User user = userDao.findByMobile(username);Collection<? extends GrantedAuthority> authorities = new ArrayList<>();return new org.springframework.security.core.userdetails.User(user.getMobile(),"{noop}"+user.getPassword(),// 這里{noop}前綴代表不進行加密,也就是匹配時與數據庫中的明文相同即可true,true,true,true,authorities);}
}
4)進行登錄測試

6.漏洞保護

6.1 csrf跨域保護請求禁用

如果不禁用csrf,那么所有的post請求均會被拒絕

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extendsWebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) {http.csrf(csrf -> csrf.disable());}
}

springsecurity實戰應用

1. 構建項目

項目框架

在這里插入圖片描述

配置文件

pom
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>javax.xml.bind</groupId><artifactId>jaxb-api</artifactId><version>2.3.1</version></dependency><!--jwt依賴--><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.0</version></dependency><!-- https://mvnrepository.com/artifact/cn.hutool/hutool-jwt --><dependency><groupId>cn.hutool</groupId><artifactId>hutool-jwt</artifactId><version>5.8.27</version></dependency><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-test</artifactId><scope>test</scope></dependency>
yml
server:port: 11012spring:datasource:url: jdbc:mysql://localhost:3306/springsecurityusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driverjpa:hibernate:ddl-auto: updateshow-sql: true

實體類

@Data
@Entity
@Table(name = "sys_user")
public class User {@Column(name = "user_id", unique = true, nullable = false, insertable = false, updatable = false)@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private int userId;@Column(name = "mobile")private String mobile;@Column(name = "pwd")private String password;@Column(name="identity")private int identity;@Column(name="nick_name")private String nickName;
}

在執行項目之后,會自動構建數據庫,在構建好數據庫之后

記得手動插入一條數據
在這里插入圖片描述

dao層

@Repository
public interface UserDao extends JpaRepository<User,Integer> {User findByMobileAndPassword(String mobile,String pwd);User findByMobile(String mobile);
}

服務層

public interface UserService {String login(String username,String password);
}
@Service
public class UserServiceImpl implements UserService {UserDao userDao;public UserServiceImpl(UserDao userDao) {this.userDao = userDao;}@Overridepublic String login(String username, String password) {User user = userDao.findByMobileAndPassword(username, password);if (user!= null) {return "login success"+user.getNickName();} else {return "login fail";}}
}

控制層

@RestController
@RequestMapping("/user")
public class UserController {UserService userService;public UserController(UserService userService) {this.userService = userService;}@GetMapping("/test")public String test() {return "tt";}@GetMapping("/login")public String login(@RequestParam(name = "account") String account, @RequestParam(name = "password") String password) {return userService.login(account, password);}
}

springSecurity


WebSecurityConfig
@Configuration
public class WebSecurityConfig {//加密器@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}// 授權管理器@Beanpublic AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception {return authConfig.getAuthenticationManager();}@Bean@Order(1)public SecurityFilterChain apiFilterChain(HttpSecurity http) throws Exception {http.securityMatcher("/user/login").authorizeHttpRequests(authorize -> authorize.anyRequest().anonymous()  // 允許匿名訪問 /user/login);return http.build();}//Spring Security過濾鏈@Beanpublic SecurityFilterChain otherFilterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests(authorize -> authorize.anyRequest().hasRole("ADMIN")).httpBasic(withDefaults());return http.build();}}

Order的大小用于指明在第幾層,越小越靠上,可以理解為優先級,越小越大

如果不設置order,那么會按照先后順序進行配置

  • 首先請求先通過order為1的過濾鏈,就是/user/login的請求,設置為允許匿名訪問
  • 而對于沒有設置過濾鏈的請求,就會使用第二個配置otherFilterChain。這個配置被認為在 apiFilterChain 之后,因為它的 @Order 值在 1 之后(沒有 @Order 默認為最后)
DBUserDetailsManager

繼承了UserDetailsService,當加載用戶的時候,就會執行這里的loadUserByUsername

@Component
public class DBUserDetailsManager implements UserDetailsService {@Resourceprivate UserDao userDao;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {User user = userDao.findByMobile(username);if (user == null) {throw new UsernameNotFoundException(username);}return new MyUserDetail(user);}
}
MyUserDetail

新建自己的UserDetails,繼承原來的UserDetails,在里面添加我們自己定義的用戶類,這樣可以方便的存儲我們自己的用戶信息

@Data
@NoArgsConstructor
@AllArgsConstructor
public class MyUserDetail implements UserDetails {private User user; // 這是自己定義的用戶類@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {return null;}@Overridepublic String getPassword() {return user.getPassword();}@Overridepublic String getUsername() {return user.getMobile();}@Overridepublic boolean isAccountNonExpired() {return true;}@Overridepublic boolean isAccountNonLocked() {return true;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return true;}
}

2. 初步測試

在上面已經完成了接口的簡單控制

我們可以通過訪問localhost:11012/user/login?account=123&password=123

發現可以訪問,并且登錄成功

但是當我們訪問localhost:11012/user/test

需要我們進行springsecurity的登錄

BasicAuthenticationFilter

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

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

相關文章

別再手工縫合API了!開源LLMOps神器LMForge,讓你像搭積木一樣玩轉AI智能體!

你是否受夠了這些&#xff1f; 剛調通OpenAI的API&#xff0c;老板說“咱們試試國產模型降本增效”&#xff0c;你看著滿屏的if-else只想說“我暈”。想給AI加上“查天氣”、“執行代碼”的能力&#xff0c;卻發現Function Calling的代碼復雜得讓人頭皮發麻。本地的Agentdemo驚…

window使用ffmep工具,加自定義腳本執行視頻轉碼成h264(運營人員使用)

技術文章大綱&#xff1a;ffmep配合腳本使用1. 需要提供腳本給視頻轉碼的給運營,給運營上傳視頻使用安裝ffmep windows版本(目前我使用的就是windows)將腳本里面的執行路徑修改成自己的電腦安裝ffmep/bin/ffmep.exe路徑處理好之后就點擊執行2.環境準備ffmep windows版解壓到一個…

Leetcode 240. 搜索二維矩陣 II 矩陣 / 二分

原題鏈接&#xff1a; Leetcode 240. 搜索二維矩陣 II 解法一&#xff1a;排除法 參考 【圖解】排除法&#xff0c;一圖秒懂&#xff01;&#xff08;Python/Java/C/C/Go/JS/Rust&#xff09; 從右上角&#xff1a; class Solution { public:bool searchMatrix(vector<vec…

OCR 證件識別:驅動澳門酒店自助入住智能化

澳門酒店作為國際旅游窗口&#xff0c;每日接待持多元證件的旅客&#xff0c;OCR 證件識別技術的應用&#xff0c;讓自助入住終端實現 “一證通辦”&#xff0c;大幅提升服務效率。?旅客在自助終端辦理入住時&#xff0c;只需將護照、港澳通行證、回鄉證、電子身份證等證件貼近…

深入解析匯編語言的奧秘

匯編語言簡介匯編語言&#xff08;Assembly Language&#xff09;是一種低級編程語言&#xff0c;直接對應計算機的機器指令集。它通過助記符&#xff08;如 MOV、ADD&#xff09;代替二進制操作碼&#xff0c;更接近硬件架構&#xff0c;常用于性能優化、嵌入式開發或逆向工程…

Nextcloud 實戰:打造屬于你的私有云與在線協作平臺

隨著數據安全與隱私保護意識的提升&#xff0c;越來越多的個人和組織選擇自建云平臺來替代公有云。Nextcloud 作為一款開源的文件同步與協作套件&#xff0c;不僅能實現類似網盤的文件存儲與分享&#xff0c;還提供日歷、聯系人、即時通訊、在線文檔編輯等協作功能&#xff0c;…

實踐指南:利用衡石AI Data Agent實現自然語言驅動的指標開發與歸因

在數字化轉型的深水區&#xff0c;企業數據團隊常面臨兩難困境&#xff1a;業務部門需要敏捷響應的指標分析&#xff0c;但傳統BI工具依賴技術團隊編寫SQL&#xff0c;導致需求交付周期長達數周&#xff1b;而直接暴露底層數據又存在安全與合規風險。衡石科技推出的AI Data Age…

知微集:Python中的線程(三)

歡迎來到"一起學點什么吧"的合集「NLP知微集」。在這里&#xff0c;我們不愿宏大敘事&#xff0c;只聚焦于自然語言處理領域中那些細微卻關鍵的“齒輪”與“螺絲釘”。我相信&#xff0c;真正深刻的理解&#xff0c;源于對細節的洞察。本期&#xff0c;我將為您拆解的…

動態規劃入門:從記憶化搜索到動態規劃

在開始對動態規劃的講解之前&#xff0c;我們需要先對記憶化搜索進行回顧&#xff1a; 什么是記憶化搜索&#xff1f; 在搜索過程中&#xff0c;當搜索樹中存在大量重復的節點時&#xff0c;我們可以通過引入一個"備忘錄"&#xff08;通常是一個數組或哈希表&#…

Boost搜索引擎 網絡庫與前端(4)

文章目錄前言一、引入網絡庫模塊引入cpp-httplibcpp-httplib測試正式編寫http_server二、前端模塊三、項目的可能拓展總結前言 終于到了最后一篇嘍&#xff0c;嘻嘻&#xff01; 一、引入網絡庫模塊 引入cpp-httplib 下載地址如下&#xff0c;我個人不喜歡新版本 ??cpp-http…

Flink反壓問題

背景在使用flink的過程中&#xff0c;多次遇到過反壓&#xff08;backpressure&#xff09;的問題&#xff0c;這通常是因為數據處理的速率超過了數據源或下游系統的處理能力導致。反壓的底層剖析網絡流控一個重要的概念是網絡流控&#xff0c;如上圖&#xff0c;不同的Consume…

Day5-中間件與請求處理

昨天搞定了異步優化&#xff0c;今天來解決一些實際問題。Day4的API雖然性能不錯&#xff0c;但還缺少一些企業級應用必備的功能。 現在的問題 前端無法訪問API&#xff08;跨域問題&#xff09;沒有請求日志&#xff0c;出問題難以排查錯誤信息格式不統一缺少統一的請求處理機…

【LeetCode熱題100道筆記】反轉鏈表

題目描述 給你單鏈表的頭節點 head &#xff0c;請你反轉鏈表&#xff0c;并返回反轉后的鏈表。 示例 1&#xff1a;輸入&#xff1a;head [1,2,3,4,5] 輸出&#xff1a;[5,4,3,2,1] 示例 2&#xff1a;輸入&#xff1a;head [1,2] 輸出&#xff1a;[2,1] 示例 3&#xff1a;…

Oracle:select top 5

在Oracle數據庫中實現SELECT TOP 5功能需采用特定語法&#xff0c;因其原生不支持TOP關鍵字。以下是兩種主流實現方式&#xff1a;?ROWNUM結合子查詢?先通過子查詢排序數據&#xff0c;再在外層用ROWNUM限制行數&#xff1a;SELECT * FROM ( SELECT * FROM 表名 ORDER BY 排序…

Kubernetes(k8s) 增量更新 po

文章目錄前言k8s 增量更新 po1. 導出要新建po 的控制器配置2. 配置詳解3. 重新生效前言 如果您覺得有用的話&#xff0c;記得給博主點個贊&#xff0c;評論&#xff0c;收藏一鍵三連啊&#xff0c;寫作不易啊^ _ ^。 ??而且聽說點贊的人每天的運氣都不會太差&#xff0c;實在…

基于stm32的車輛安全駕駛預警系統

若該文為原創文章&#xff0c;轉載請注明原文出處。一、 項目背景與引言(一) 研究背景及意義道路交通安全是全球性的重大公共安全問題。據統計&#xff0c;絕大多數交通事故源于駕駛員的危險狀態&#xff08;疲勞、分心、健康突發狀況&#xff09;和危險駕駛行為&#xff08;超…

React學習教程,從入門到精通, React 新創建組件語法知識點及案例代碼(11)

React 新創建組件語法知識點及案例代碼 React 是由 Facebook 開發的一個用于構建用戶界面的 JavaScript 庫。隨著 React 的不斷發展&#xff0c;創建組件的方式也在不斷演進。本文將詳細介紹 React 中創建組件的最新語法&#xff0c;包括函數組件&#xff08;Functional Compo…

SQL Server全鏈路安全防護

SQL Server 的安全性是一個多層次、綜合性的體系&#xff0c;旨在保護數據免受未授權訪問、篡改和泄露。其核心安全機制可概括為以下幾個方面&#xff1a;1. 身份驗證&#xff08;Authentication&#xff09; Windows 身份驗證&#xff1a; 使用 Windows 賬戶&#xff08;域/本…

如何利用Web3提升企業競爭力

在這個信息爆炸的時代&#xff0c;Web3技術以其獨特的去中心化、透明性和用戶主權特性&#xff0c;成為企業提升競爭力的新戰場。本文將深入探討企業如何把握Web3的浪潮&#xff0c;實現業務的飛躍。 1. 把握Web3的核心價值 Web3的核心在于去中心化、透明性和用戶主權。這種模式…

HOW - 在瀏覽器下載一個 Excel 表格文件

文章目錄一、技術方案二、前端具體實現代碼分析轉換邏輯注意事項一、技術方案 后臺返回 base64 數據 {code: 0,data: "base64;...", }前端進行數據格式轉化并下載成 Excel 文件 這篇文章主要介紹第二個步驟的實現。 二、前端具體實現 代碼 src/utils/transform…