從Redis反序列化UserDetails對象異常后發現FastJson序列化的一些問題

? ? ? ? 最近在使用SpringSecurity+JWT實現認證授權的時候,出現Redis在反序列化userDetails的異常。通過實踐發現,使用不同的序列化方法和不同的fastJson版本,異常信息各不相同。所以特地記錄了下來。

一、項目代碼

? ? ? ? 先來看看我項目中redis相關配置信息。

1.自定義的redis序列化器

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.TypeFactory;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;
import com.alibaba.fastjson.parser.ParserConfig;
import org.springframework.util.Assert;
import java.nio.charset.Charset;/*** Redis使用FastJson序列化** @author mosul*/
public class FastJsonRedisSerializer<T> implements RedisSerializer<T>
{public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");private Class<T> clazz;static{ParserConfig.getGlobalInstance().setAutoTypeSupport(true);}public FastJsonRedisSerializer(Class<T> clazz){super();this.clazz = clazz;}@Overridepublic byte[] serialize(T t) throws SerializationException{if (t == null){return new byte[0];}return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);}@Overridepublic T deserialize(byte[] bytes) throws SerializationException{if (bytes == null || bytes.length <= 0){return null;}String str = new String(bytes, DEFAULT_CHARSET);return JSON.parseObject(str, clazz);}protected JavaType getJavaType(Class<?> clazz){return TypeFactory.defaultInstance().constructType(clazz);}
}

2.redis配置類

import com.alibaba.fastjson.support.spring.GenericFastJsonRedisSerializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;@Configuration
public class RedisConfig {/*** 指定特定的連接工廠* @return*//*@Beanpublic RedisConnectionFactory redisConnectionFactory() {return new LettuceConnectionFactory();}*/@Bean@SuppressWarnings(value = { "unchecked", "rawtypes" })public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory){RedisTemplate<Object, Object> template = new RedisTemplate<>();template.setConnectionFactory(connectionFactory);FastJsonRedisSerializer serializer = new FastJsonRedisSerializer(Object.class);// 使用StringRedisSerializer來序列化和反序列化redis的key值template.setKeySerializer(new StringRedisSerializer());template.setValueSerializer(serializer);// Hash的key也采用StringRedisSerializer的序列化方式template.setHashKeySerializer(new StringRedisSerializer());template.setHashValueSerializer(serializer);template.afterPropertiesSet();return template;}
}

3.redis工具類

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundSetOperations;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;import java.util.*;
import java.util.concurrent.TimeUnit;/*** Redis幫助類** @author mosul*/
@SuppressWarnings(value = { "unchecked", "rawtypes" })
@Component
public class RedisHelper
{@Autowiredpublic RedisTemplate redisTemplate;/*** 緩存基本的對象,Integer、String、實體類等** @param key 緩存的鍵值* @param value 緩存的值*/public <T> void setCacheObject(final String key, final T value){redisTemplate.opsForValue().set(key, value);}/*** 緩存基本的對象,Integer、String、實體類等** @param key 緩存的鍵值* @param value 緩存的值* @param timeout 時間* @param timeUnit 時間顆粒度*/public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit){redisTemplate.opsForValue().set(key, value, timeout, timeUnit);}/*** 設置有效時間** @param key Redis鍵* @param timeout 超時時間* @return true=設置成功;false=設置失敗*/public boolean expire(final String key, final long timeout){return expire(key, timeout, TimeUnit.SECONDS);}/*** 設置有效時間** @param key Redis鍵* @param timeout 超時時間* @param unit 時間單位* @return true=設置成功;false=設置失敗*/public boolean expire(final String key, final long timeout, final TimeUnit unit){return redisTemplate.expire(key, timeout, unit);}/*** 獲得緩存的基本對象。** @param key 緩存鍵值* @return 緩存鍵值對應的數據*/public <T> T getCacheObject(final String key){ValueOperations<String, T> operation = redisTemplate.opsForValue();return operation.get(key);}/*** 刪除單個對象** @param key*/public boolean deleteObject(final String key){return redisTemplate.delete(key);}/*** 刪除集合對象** @param collection 多個對象* @return*/public long deleteObject(final Collection collection){return redisTemplate.delete(collection);}/*** 緩存List數據** @param key 緩存的鍵值* @param dataList 待緩存的List數據* @return 緩存的對象*/public <T> long setCacheList(final String key, final List<T> dataList){Long count = redisTemplate.opsForList().rightPushAll(key, dataList);return count == null ? 0 : count;}/*** 獲得緩存的list對象** @param key 緩存的鍵值* @return 緩存鍵值對應的數據*/public <T> List<T> getCacheList(final String key){return redisTemplate.opsForList().range(key, 0, -1);}/*** 緩存Set** @param key 緩存鍵值* @param dataSet 緩存的數據* @return 緩存數據的對象*/public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet){BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);Iterator<T> it = dataSet.iterator();while (it.hasNext()){setOperation.add(it.next());}return setOperation;}/*** 獲得緩存的set** @param key* @return*/public <T> Set<T> getCacheSet(final String key){return redisTemplate.opsForSet().members(key);}/*** 緩存Map** @param key* @param dataMap*/public <T> void setCacheMap(final String key, final Map<String, T> dataMap){if (dataMap != null) {redisTemplate.opsForHash().putAll(key, dataMap);}}/*** 獲得緩存的Map** @param key* @return*/public <T> Map<String, T> getCacheMap(final String key){return redisTemplate.opsForHash().entries(key);}/*** 往Hash中存入數據** @param key Redis鍵* @param hKey Hash鍵* @param value 值*/public <T> void setCacheMapValue(final String key, final String hKey, final T value){redisTemplate.opsForHash().put(key, hKey, value);}/*** 獲取Hash中的數據** @param key Redis鍵* @param hKey Hash鍵* @return Hash中的對象*/public <T> T getCacheMapValue(final String key, final String hKey){HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();return opsForHash.get(key, hKey);}/*** 刪除Hash中的數據** @param key* @param hkey*/public void delCacheMapValue(final String key, final String hkey){HashOperations hashOperations = redisTemplate.opsForHash();hashOperations.delete(key, hkey);}/*** 獲取多個Hash中的數據** @param key Redis鍵* @param hKeys Hash鍵集合* @return Hash對象集合*/public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys){return redisTemplate.opsForHash().multiGet(key, hKeys);}/*** 獲得緩存的基本對象列表** @param pattern 字符串前綴* @return 對象列表*/public Collection<String> keys(final String pattern){return redisTemplate.keys(pattern);}
}

4.自己系統中的UserDetails

@Data
@NoArgsConstructor
@AllArgsConstructor
public class LoginUser implements UserDetails {private static final long serialVersionUID = 1L;// 系統用戶private SysUser user;// 用戶權限列表private List<SysPermission> permissionList;@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {return permissionList.stream().filter(permission -> permission.getPermission() != null).map(permission -> new SimpleGrantedAuthority(permission.getPermission())).collect(Collectors.toList());}@Overridepublic String getPassword() {return user.getPassword();}@Overridepublic String getUsername() {return user.getUsername();}@Overridepublic boolean isAccountNonExpired() {return true;}@Overridepublic boolean isAccountNonLocked() {return true;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return true;}
}

5.登錄設置


@Overridepublic String login(SysUser sysUser) {String token = null;//密碼需要客戶端加密后傳遞try {UserDetails userDetails = sysUserService.loadUserByUsername(sysUser.getUsername());if(!passwordEncoder.matches(sysUser.getPassword(),userDetails.getPassword())){throw new BadCredentialsException("密碼不正確");}UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());SecurityContextHolder.getContext().setAuthentication(authentication);token = jwtTokenUtil.generateToken(userDetails);String key = "login:" + sysUser.getUsername();//設置redisredisHelper.setCacheObject(key,userDetails);//insertLoginLog(username);} catch (AuthenticationException e) {LOGGER.warn("登錄異常:{}", e.getMessage());}return token;}@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {SysUser sysUser = sysUserMapper.selectOne(new QueryWrapper<SysUser>().eq("username", username));List<SysPermission> permissionsByUser = sysUserRoleMapper.findPermissionsByUser(sysUser.getUserId());sysUser.setPassword(new BCryptPasswordEncoder().encode(sysUser.getPassword()));// 將系統的用戶信息和權限信息封裝成UserDetailsUserDetails userDetail = new LoginUser(sysUser, permissionsByUser);return userDetail;}

6.JWT校驗

 @Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {String authHeader = request.getHeader(this.tokenHeader);if (authHeader != null && authHeader.startsWith(this.tokenHead)) {String authToken = authHeader.substring(this.tokenHead.length());// The part after "Bearer "String username = jwtTokenUtil.getUserNameFromToken(authToken.trim());LOGGER.info("checking username:{}", username);if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {//從redis中獲取userDetailsString redisKey = "login:" + username;UserDetails userDetails = redisHelper.getCacheObject(redisKey);if(Objects.isNull(userDetails)){throw new RuntimeException("用戶未登錄");}if (jwtTokenUtil.validateToken(authToken, userDetails)) {//存入SecurityContextHolder//TODO 獲取權限信息封裝到Authentication中UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));LOGGER.info("authenticated user:{}", username);SecurityContextHolder.getContext().setAuthentication(authentication);}}}//放行filterChain.doFilter(request, response);}

7.fastjson版本

        <!--fastjson依賴--><!--第一個版本--><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.47</version></dependency><!--第二個版本--><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>2.0.22</version></dependency>

? ? ? ? 上面的代碼中,先根據用戶名獲取用戶對應的用戶信息和權限信息,然后構建SpringSecurity的UserDetails對象,用戶登錄的時候將這個UserDetails對象放入redis中,后續校驗請求攜帶的token與redis中的信息是否一致。

二、異常信息

1.版本一報錯信息

????????需要說明的是,在redis系列化時,是正常的,對應的值也成功設置近緩存了,但是在JWT校驗階段,執行UserDetails userDetails = redisHelper.getCacheObject(redisKey);時出現異常,反序列化失敗。

????????針對這個問題,首先上面的代碼邏輯是沒有問題的,但是與fastjson反序列化不兼容導致的問題。

????????根據異常信息提示,設置屬性authorities錯誤,猜想下是因為LoginUser中沒有authorities屬性,但也說不過去,同樣沒有屬性username和password怎么不會報錯?

? ? ? ? 帶著這個疑問,我們先給將LoginUser代碼改為下面這種形式。

@Data
public class LoginUser implements UserDetails {private static final long serialVersionUID = 1L;private SysUser user;private List<SysPermission> permissionList;private List<GrantedAuthority> authorities;public LoginUser() {}public LoginUser(SysUser user, List<SysPermission> permissionList) {this(user,permissionList,null);}/*** 針對fastJson中redis反序列化報錯的改進* org.springframework.data.redis.serializer.SerializationException:* Could not deserialize: set authorities error; nested exception is com.alibaba.fastjson.JSONException: set authorities error** @param user* @param permissionList* @param authorities*/public LoginUser(SysUser user, List<SysPermission> permissionList, List<GrantedAuthority> authorities) {//返回當前用戶的權限List<GrantedAuthority> authoritieList = permissionList.stream().filter(permission -> permission.getPermission() != null).map(permission -> new SimpleGrantedAuthority(permission.getPermission())).collect(Collectors.toList());this.user = user;this.permissionList = permissionList;this.authorities = authoritieList;}@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {return this.authorities;}@Overridepublic String getPassword() {return user.getPassword();}@Overridepublic String getUsername() {return user.getUsername();}@Overridepublic boolean isAccountNonExpired() {return true;}@Overridepublic boolean isAccountNonLocked() {return true;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return true;}
}

? ? ? ? 發現這里改完之后,是可以正常運行的。 而且發現,當我們給getAuthorities()賦簡單的值的時候,不會出現這個問題。

@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {List<GrantedAuthority> authoritieList = new ArrayList<>();authoritieList.add(new SimpleGrantedAuthority("ROLE_ADMIN"));return authoritieList;}

? ? ? ? 而且我們在構造函數中提前將權限列表加載出來,賦值給一個屬性,屬性不一定非得名為authorities,如屬性名為authoritiesList。然后返回這個屬性也不會報錯。

@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {return this.authoritieList;}

? ? ? ? 通過上面兩個測試可以猜測下,如果構造函數中提前將比較復雜的實現暴露了,反系列化也不會報錯。可能出現異常的原因fastjson是對不確定結果無法反系列化,如果是簡單的結果或在構造器中就已經知道了確定結果,就不會出現反序列化的異常。?

2.版本二報錯信息

? ? ? ? ?在使用fastJson 2.x版本的時候,同時需要對redis配置類做如下修改。

@Bean@SuppressWarnings(value = { "unchecked", "rawtypes" })public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory){RedisTemplate<Object, Object> template = new RedisTemplate<>();template.setConnectionFactory(connectionFactory);/*java.lang.ClassCastException:* com.alibaba.fastjson.JSONObject cannot be cast to org.springframework.security.core.userdetails.UserDetails* */String[] acceptNames = {"org.springframework.security.core.authority.SimpleGrantedAuthority"};GenericFastJsonRedisSerializer serializer = new GenericFastJsonRedisSerializer(acceptNames);// 使用StringRedisSerializer來序列化和反序列化redis的key值template.setKeySerializer(new StringRedisSerializer());template.setValueSerializer(serializer);// Hash的key也采用StringRedisSerializer的序列化方式template.setHashKeySerializer(new StringRedisSerializer());template.setHashValueSerializer(serializer);template.afterPropertiesSet();return template;}

? ? ? ? 修改完成之后,還是會出現設置屬性authorities錯誤,同樣需要對LoginUser做上述修改。

3.發現FastJson反系列的一般問題

? ? ? ? 正如上面所說的,同樣沒有屬性username和password怎么不會報錯?于是做了一系列測試。發現了在低版本的fastJson中,對應集合類型接口方法中包含較復雜的實現(不是直接顯示賦值),反序化可能要求必須有對應的屬性。

? ? ? ? 定義了一個有不同返回值類型的幾種方法來測試。

public interface CrazyDetails {List<String> getApps();User getUser();String getName();String[] getNodes();Collection<String> getTests();List<User> getUsers();
}

????????定義一個實現類

@Data
@NoArgsConstructor
@AllArgsConstructor
public class MosulApp implements CrazyDetails{@Overridepublic User getUser() {return new User(this.appInfo.name);}private AppInfo appInfo;@Overridepublic List<User> getUsers() {List<User> userList = new ArrayList<>();for(int i = 0; i < this.appInfo.name.length(); i ++) {User user1 = new User("" + i);userList.add(user1);}return userList;}@Overridepublic String[] getNodes() {String[] strings = new String[2];strings = new String[]{this.appInfo.name,this.appInfo.details};return strings;}/*private List<String> apps;*///報錯,添加需要private List<String> tests@Overridepublic Collection<String> getTests() {List<String> list = Arrays.asList(appInfo.name);return list;}@Overridepublic List<String> getApps() {List<String> objects = new ArrayList<>();for(int i = 0; i < this.appInfo.name.length(); i ++) {objects.add("tt" + i);}return objects;}@Overridepublic String getName() {return this.appInfo.name;}}

? ? ? ?

????????在測試發現fastJson 1.x版本對于Arrays.asList(appInfo.name);反序列化失敗,fastJson 2.x版本則可以反序列化成功,但對于UserDetails中Collection<? extends GrantedAuthority> getAuthorities()中如果有比較復雜的實現,fastJson 2.x版本反序列化還是會失敗。所以為了保險起見,最后在自定義的UserDetails中添加authorities屬性,除了這種方法能外,應該也跟自定義的序列化器相關設置有關,需要進行探索。

三、Redis和SpringSecutiry相關配置

? ? ? ? 基于上述測試,最終fastJson選用2.0.22版本,最后將redis配置類和SpringSecutiry中UserDetails實現類修改為如下所示。

1.redis配置類

@Configuration
public class RedisConfig {/*** 指定特定的連接工廠* @return*//*@Beanpublic RedisConnectionFactory redisConnectionFactory() {return new LettuceConnectionFactory();}*/@Bean@SuppressWarnings(value = { "unchecked", "rawtypes" })public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory){RedisTemplate<Object, Object> template = new RedisTemplate<>();template.setConnectionFactory(connectionFactory);/* FastJsonRedisSerializer serializer = new FastJsonRedisSerializer(Object.class);*//*解決java.lang.ClassCastException:* com.alibaba.fastjson.JSONObject cannot be cast to org.springframework.security.core.userdetails.UserDetails* */String[] acceptNames = {"org.springframework.security.core.authority.SimpleGrantedAuthority"};GenericFastJsonRedisSerializer serializer = new GenericFastJsonRedisSerializer(acceptNames);// 使用StringRedisSerializer來序列化和反序列化redis的key值template.setKeySerializer(new StringRedisSerializer());template.setValueSerializer(serializer);// Hash的key也采用StringRedisSerializer的序列化方式template.setHashKeySerializer(new StringRedisSerializer());template.setHashValueSerializer(serializer);template.afterPropertiesSet();return template;}
}

2.LoginUser類

@Data
public class LoginUser implements UserDetails {private static final long serialVersionUID = 1L;// 用戶信息private SysUser user;// 用戶權限列表private List<SysPermission> permissionList;// SpringSecurity對應的權限信息private List<GrantedAuthority> authorities;public LoginUser() {}public LoginUser(SysUser user, List<SysPermission> permissionList) {this(user,permissionList,null);}/*** 針對fastJson中redis反序列化報錯的改進* org.springframework.data.redis.serializer.SerializationException:* Could not deserialize: set authorities error; nested exception is com.alibaba.fastjson.JSONException: set authorities error** @param user* @param permissionList* @param authorities*/public LoginUser(SysUser user, List<SysPermission> permissionList, List<GrantedAuthority> authorities) {//返回當前用戶的權限List<GrantedAuthority> authoritieList = permissionList.stream().filter(permission -> permission.getPermission() != null).map(permission -> new SimpleGrantedAuthority(permission.getPermission())).collect(Collectors.toList());this.user = user;this.permissionList = permissionList;this.authorities = authoritieList;}@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {return this.authorities;}@Overridepublic String getPassword() {return user.getPassword();}@Overridepublic String getUsername() {return user.getUsername();}@Overridepublic boolean isAccountNonExpired() {return true;}@Overridepublic boolean isAccountNonLocked() {return true;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return true;}
}

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

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

相關文章

黑馬點評筆記 redis緩存三大問題解決

文章目錄 緩存問題緩存穿透問題的解決思路編碼解決商品查詢的緩存穿透問題 緩存雪崩問題及解決思路緩存擊穿問題及解決思路問題分析使用鎖來解決代碼實現 邏輯過期方案代碼實現 緩存問題 我們熟知的是用到緩存就會遇到緩存三大問題&#xff1a; 緩存穿透緩存擊穿緩存雪崩 接…

QOverload獲取重載的信號

QOverload獲取重載的信號 多個信號或者函數同名&#xff0c;但是不同參數&#xff0c;也就是存在重載 可以使用QOverload獲取指定的重載函數 QOverload<int>::of(&QComboBox::currentIndexChanged)上面的代碼就是用來獲取參數為int的那個函數

【Spring篇】JDK動態代理

目錄 什么是代理&#xff1f; 代理模式 動態代理 Java中常用的代理模式 問題來了&#xff0c;如何動態生成代理類&#xff1f; 動態代理底層實現 什么是代理&#xff1f; 顧名思義&#xff0c;代替某個對象去處理一些問題&#xff0c;謂之代理&#xff0c;那么何為動態&a…

短視頻賬號矩陣系統saas化批量管理部署搭建/技術

一、短視頻矩陣系統建模----技術api接口--獲取用戶授權 技術文檔分享&#xff1a; 本系統采用MySQL數據庫進行存儲&#xff0c;數據庫設計如下&#xff1a; 1.用戶表&#xff08;user&#xff09;&#xff1a; - 用戶ID&#xff08;user_id&#xff09; - 用戶名&#xff08;…

SELinux零知識學習二十七、SELinux策略語言之類型強制(12)

接前一篇文章:SELinux零知識學習二十六、SELinux策略語言之類型強制(11) 二、SELinux策略語言之類型強制 4. 類型規則 類型規則在創建客體或在運行過程中重新標記時指定其默認類型。在策略語言中定義了兩個類型規則: type_transtition在域轉換過程中標記行為發生時以及創…

詳解Vue中的computed和watch

詳解Vue中的computed和watch 前言原理computedcomputed特點computed有幾種創建方式應用 WatchWatch有幾種創建方式Watch主要內容Watch特性應用場景 computed和Watch區別 前言 在Vue當中&#xff0c;watch和computed都可以實現監聽的效果&#xff0c;本文主要是圍繞watch和comp…

【理解ARM架構】操作寄存器實現UART | 段的概念 | IDE背后的命令

&#x1f431;作者&#xff1a;一只大喵咪1201 &#x1f431;專欄&#xff1a;《理解ARM架構》 &#x1f525;格言&#xff1a;你只管努力&#xff0c;剩下的交給時間&#xff01; 目錄 &#x1f360;操作寄存器實現UART&#x1f35f;UART原理&#x1f35f;編程 &#x1f360;…

python——第十二天

內置模塊或者其他模塊學習方式&#xff1a; dir help os模塊負責程序與操作系統的交互&#xff0c;提供了訪問操作系統底層的接口&#xff1b;即os模塊提供了非常豐富的方法用來處理文件和目錄。 os&#xff1a; os.path 遍歷C盤代碼 import os from os import path def …

修改YOLOv5的模型結構第三彈

&#x1f368; 本文為&#x1f517;365天深度學習訓練營 中的學習記錄博客&#x1f356; 原作者&#xff1a;K同學啊 | 接輔導、項目定制&#x1f680; 文章來源&#xff1a;K同學的學習圈子 文章目錄 任務任務拆解 開始修改C2模塊修改yolo.py修改模型配置文件 模型訓練 上次已…

【工具使用】Keil工具的使用——常用配置介紹

Keil調試具體教程學習 目錄 ???????Keil調試具體教程學習 常用功能總結 &#xff08;2&#xff09;目標設置&#xff08;Target&#xff09; ①設置晶振頻率 ②跨模塊優化選項 ③微庫選項 &#xff08;3&#xff09;輸出設置&#xff08;Output&#xff09; ①…

插入排序(形象類比)

最近在看riscv手冊的時候&#xff0c;里面有一段代碼是插入排序&#xff0c;但是單看代碼的時候有點迷&#xff0c;沒看懂咋操作的&#xff0c;后來又查資料復習了一下&#xff0c;最終才把代碼看明白&#xff0c;所以寫篇博客記錄一下。 插入排序像打撲克牌 這是我聽到過比較形…

list的總結

目錄 1.什么是list 1.1list 的優勢和劣勢 優勢&#xff1a; 劣勢&#xff1a; 2.構造函數 2.1 default (1) 2.2 fill (2) 2.3 range (3) 2.4 copy (4) 3.list iterator的使用 3.1. begin() 3.2. end() 3.3迭代器遍歷 4. list容量函數 4.1. empty() 4.2. siz…

語音合成綜述Speech Synthesis

一、語音合成概述 語音信號的產生分為兩個階段&#xff0c;信息編碼和生理控制。首先在大腦中出現某種想要表達的想法&#xff0c;然后由大腦將其編碼為具體的語言文字序列&#xff0c;及語音中可能存在的強調、重讀等韻律信息。經過語言的組織&#xff0c;大腦通過控制發音器…

正整數分解

題目編號&#xff1a;Exp08-Basic01&#xff0c;GJBook3-12-05 題目名稱&#xff1a;正整數分解 題目描述&#xff1a;正整數n&#xff0c;按第一項遞減的順序依次輸出其和等于n的所有不增的正整數和式。 輸入&#xff1a;一個正整數n&#xff08;0<n≤15&#xff09;。 …

qRT-PCR相對定量計算詳解qPCR相對定量計算方式——2^-(??Ct) deta t

做完轉錄組分析之后&#xff0c;一般都要求做qRT-PCR來驗證二代測序得到的轉錄本表達是否可靠。熒光定量PCR是一種相對表達定量的方法&#xff0c;他的計算方法有很多&#xff0c;常用的相對定量數據分析方法有雙標曲線法&#xff0c;ΔCt法&#xff0c;2^-ΔΔCt法(Livak法)&a…

順序表基本操作全面解析

文章目錄 1.線性表2.順序表分類2.1 靜態順序表2.2 動態順序表 3. 順序表各接口實現1. 定義結構體(Seqlist)2. 結構體初始化(SLInit)3.檢查容量 (SLCheckCapacity)4.打印數據 (SLPrintf)5.插入操作5.1 從數據頭部插入(SLPushFront)5.2 從數據尾部插入(SLPushBack)5.3 從任意下標…

100天精通Python(可視化篇)——第106天:Pyecharts繪制多種炫酷桑基圖參數說明+代碼實戰

文章目錄 專欄導讀一、桑基圖介紹1. 桑基圖是什么?2. 桑基圖應用場景?二、桑基圖配置選項1. 導包2. add函數3. 分層設置三、桑基圖基礎1. 普通桑基圖2. 修改標簽位置3. 修改節點布局方向4、月度開支桑基圖書籍推薦專欄導讀 ????本文已收錄于《100天精通Python從入門到就…

線性表之順序表

文章目錄 主要內容一.順序表1.插入操作&#xff1a;代碼如下&#xff08;示例&#xff09;: 2.刪除操作&#xff1a;代碼如下&#xff08;示例&#xff09;: 3.按值查找&#xff1a;代碼如下&#xff08;示例&#xff09;: 總結 主要內容 順序表 預備知識 定義&#xff1a; 線…

GEE:基于 Landst 遙感數據計算的 kNDVI 下載 APP

作者&#xff1a;CSDN _養樂多_ 本文記錄了在Google Earth Engine&#xff08;GEE&#xff09;平臺中&#xff0c;使用 Landsat 遙感數據計算并且下載 kNDVI 的應用 APP 鏈接&#xff0c;并介紹該 APP 的使用方法和步驟。該APP可以為用戶展示 NDVI 和 kNDVI 的遙感影像&#…

抽象類, 接口, Object類 ---java

目錄 一. 抽象類 1.1 抽象類概念 1.2 抽象類語法 1.3 抽象類特性 1.4 抽象類的作用 二. 接口 2.1 接口的概念 2.2 語法規則 2.3 接口的使用 2.4 接口間的繼承 2.5 抽象類和接口的區別 三. Object類 3.1 toString() 方法 3.2 對象比較equals()方法 3.3 hash…