Spring security (一)架構框架-Component、Service、Filter分析

??想要深入spring security的authentication (身份驗證)和access-control(訪問權限控制)工作流程,必須清楚spring security的主要技術點包括關鍵接口、類以及抽象類如何協同工作進行authentication 和access-control的實現。

1.spring security 認證和授權流程

常見認證和授權流程可以分成:

  1. A user is prompted to log in with a username and password (用戶用賬密碼登錄)
  2. The system (successfully) verifies that the password is correct for the username(校驗密碼正確性)
  3. The context information for that user is obtained (their list of roles and so on).(獲取用戶信息context,如權限)
  4. A security context is established for the user(為用戶創建security context)
  5. The user proceeds, potentially to perform some operation which is potentially protected by an access control mechanism which checks the required permissions for the operation against the current security context information.(訪問權限控制,是否具有訪問權限)

1.1 spring security 認證

上述前三點為spring security認證驗證環節:

  1. 通常通過AbstractAuthenticationProcessingFilter過濾器將賬號密碼組裝成Authentication實現類UsernamePasswordAuthenticationToken;
  2. 將token傳遞給AuthenticationManager驗證是否有效,而AuthenticationManager通常使用ProviderManager實現類來檢驗;
  3. AuthenticationManager認證成功后將返回一個擁有詳細信息的Authentication object(包括權限信息,身份信息,細節信息,但密碼通常會被移除);
  4. 通過SecurityContextHolder.getContext().getAuthentication().getPrincipal()將Authentication設置到security context中。

1.2 spring security訪問授權

  1. 通過FilterSecurityInterceptor過濾器入口進入;
  2. FilterSecurityInterceptor通過其繼承的抽象類的AbstractSecurityInterceptor.beforeInvocation(Object object)方法進行訪問授權,其中涉及了類AuthenticationManager、AccessDecisionManager、SecurityMetadataSource等。

根據上述描述的過程,我們接下來主要去分析其中涉及的一下Component、Service、Filter。

2.核心組件(Core Component )

2.1 SecurityContextHolder

??SecurityContextHolder提供對SecurityContext的訪問,存儲security context(用戶信息、角色權限等),而且其具有下列儲存策略即工作模式:

  1. SecurityContextHolder.MODE_THREADLOCAL(默認):使用ThreadLocal,信息可供此線程下的所有的方法使用,一種與線程綁定的策略,此天然很適合Servlet Web應用。

  2. SecurityContextHolder.MODE_GLOBAL:使用于獨立應用

  3. SecurityContextHolder.MODE_INHERITABLETHREADLOCAL:具有相同安全標示的線程

修改SecurityContextHolder的工作模式有兩種方法 :

  1. 設置一個系統屬性(system.properties) : spring.security.strategy;
  2. 調用SecurityContextHolder靜態方法setStrategyName()

在默認ThreadLocal策略中,SecurityContextHolder為靜態方法獲取用戶信息為:

  Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();if (principal instanceof UserDetails) {      String username = ((UserDetails)principal).getUsername();} else {String username = principal.toString();}
復制代碼

但是一般不需要自身去獲取。 其中getAuthentication()返回一個Authentication認證主體,接下來分析Authentication、UserDetails細節。

2.2 Authentication

??Spring Security使用一個Authentication對象來描述當前用戶的相關信息,其包含用戶擁有的權限信息列表、用戶細節信息(身份信息、認證信息)。Authentication為認證主體在spring security中時最高級別身份/認證的抽象,常見的實現類UsernamePasswordAuthenticationToken。Authentication接口源碼:

public interface Authentication extends Principal, Serializable { //權限信息列表,默認GrantedAuthority接口的一些實現類Collection<? extends GrantedAuthority> getAuthorities(); //密碼信息Object getCredentials();//細節信息,web應用中的實現接口通常為 WebAuthenticationDetails,它記錄了訪問者的ip地址和sessionId的值Object getDetails();//通常返回值為UserDetails實現類Object getPrincipal();boolean isAuthenticated();void setAuthenticated(boolean var1) throws IllegalArgumentException;
}
復制代碼

前面兩個組件都涉及了UserDetails,以及GrantedAuthority其到底是什么呢?2.3小節分析。

2.3 UserDetails&GrantedAuthority

??UserDetails提供從應用程序的DAO或其他安全數據源構建Authentication對象所需的信息,包含GrantedAuthority。其官方實現類為User,開發者可以實現其接口自定義UserDetails實現類。其接口源碼:

 public interface UserDetails extends Serializable {Collection<? extends GrantedAuthority> getAuthorities();String getPassword();String getUsername();boolean isAccountNonExpired();boolean isAccountNonLocked();boolean isCredentialsNonExpired();boolean isEnabled();
}
復制代碼

??UserDetails與Authentication接口功能類似,其實含義即是Authentication為用戶提交的認證憑證(賬號密碼),UserDetails為系統中用戶正確認證憑證,在UserDetailsService中的loadUserByUsername方法獲取正確的認證憑證。 ??其中在getAuthorities()方法中獲取到GrantedAuthority列表是代表用戶訪問應用程序權限范圍,此類權限通常是“role(角色)”,例如ROLE_ADMINISTRATOR或ROLE_HR_SUPERVISOR。GrantedAuthority接口常見的實現類SimpleGrantedAuthority。

3. 核心服務類(Core Services)

3.1 AuthenticationManager、ProviderManager以及AuthenticationProvider

??AuthenticationManager是認證相關的核心接口,是認證一切的起點。但常見的認證流程都是AuthenticationManager實現類ProviderManager處理,而且ProviderManager實現類基于委托者模式維護AuthenticationProvider 列表用于不同的認證方式。例如:

  1. 使用賬號密碼認證方式DaoAuthenticationProvider實現類(繼承了AbstractUserDetailsAuthenticationProvide抽象類),其為默認認證方式,進行數據庫庫獲取認證數據信息。
  2. 游客身份登錄認證方式AnonymousAuthenticationProvider實現類
  3. 從cookies獲取認證方式RememberMeAuthenticationProvider實現類

??AuthenticationProvider為

ProviderManager源碼分析:

public Authentication authenticate(Authentication authentication)throws AuthenticationException {Class<? extends Authentication> toTest = authentication.getClass();AuthenticationException lastException = null;Authentication result = null;//AuthenticationProvider列表依次認證for (AuthenticationProvider provider : getProviders()) {if (!provider.supports(toTest)) {continue;}try {//每個AuthenticationProvider進行認證result = provider.authenticate(authentication)if (result != null) {copyDetails(authentication, result);break;}}....catch (AuthenticationException e) {lastException = e;}}//進行父類AuthenticationProvider進行認證if (result == null && parent != null) {// Allow the parent to try.try {result = parent.authenticate(authentication);}catch (AuthenticationException e) {lastException = e;}}// 如果有Authentication信息,則直接返回if (result != null) {if (eraseCredentialsAfterAuthentication&& (result instanceof CredentialsContainer)) {//清除密碼((CredentialsContainer) result).eraseCredentials();}//發布登錄成功事件eventPublisher.publishAuthenticationSuccess(result);return result;}//如果都沒認證成功,拋出異常if (lastException == null) {lastException = new ProviderNotFoundException(messages.getMessage("ProviderManager.providerNotFound",new Object[] { toTest.getName() },"No AuthenticationProvider found for {0}"));}prepareException(lastException, authentication);throw lastException;}  
復制代碼

??ProviderManager 中的AuthenticationProvider列表,會依照次序去認證,默認策略下,只需要通過一個AuthenticationProvider的認證,即可被認為是登錄成功,而且AuthenticationProvider認證成功后返回一個Authentication實體,并為了安全會進行清除密碼。如果所有認證器都無法認證成功,則ProviderManager 會拋出一個ProviderNotFoundException異常。

3.2 UserDetailsService

??UserDetailsService接口作用是從特定的地方獲取認證的數據源(賬號、密碼)。如何獲取到系統中正確的認證憑證,通過loadUserByUsername(String username)獲取認證信息,而且其只有一個方法:

UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;  
復制代碼

其常見的實現類從數據獲取的JdbcDaoImpl實現類,從內存中獲取的InMemoryUserDetailsManager實現類,不過我們可以實現其接口自定義UserDetailsService實現類,如下:

public class CustomUserService implements UserDetailsService {@Autowired//用戶mapperprivate UserInfoMapper userInfoMapper;@Autowired//用戶權限mapperprivate PermissionInfoMapper permissionInfoMapper;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {UserInfoDTO userInfo = userInfoMapper.getUserInfoByUserName(username);if (userInfo != null) {List<PermissionInfoDTO> permissionInfoDTOS = permissionInfoMapper.findByAdminUserId(userInfo.getId());List<GrantedAuthority> grantedAuthorityList = new ArrayList<>();//組裝權限GrantedAuthority objectfor (PermissionInfoDTO permissionInfoDTO : permissionInfoDTOS) {if (permissionInfoDTO != null && permissionInfoDTO.getPermissionName() != null) {GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(permissionInfoDTO.getPermissionName());grantedAuthorityList.add(grantedAuthority);}}//返回用戶信息return new User(userInfo.getUserName(), userInfo.getPasswaord(), grantedAuthorityList);}else {//拋出用戶不存在異常throw new UsernameNotFoundException("admin" + username + "do not exist");}}
}   
復制代碼

3.3 AccessDecisionManager&SecurityMetadataSource

??AccessDecisionManager是由AbstractSecurityInterceptor調用,負責做出最終的訪問控制決策。

AccessDecisionManager接口源碼:

 //訪問控制決策void decide(Authentication authentication, Object secureObject,Collection<ConfigAttribute> attrs) throws AccessDeniedException;//是否支持處理傳遞的ConfigAttributeboolean supports(ConfigAttribute attribute);//確認class是否為AccessDecisionManagerboolean supports(Class clazz);
復制代碼

??SecurityMetadataSource包含著AbstractSecurityInterceptor訪問授權所需的元數據(動態url、動態授權所需的數據),在AbstractSecurityInterceptor授權模塊中結合AccessDecisionManager進行訪問授權。其涉及了ConfigAttribute。 SecurityMetadataSource接口:

Collection<ConfigAttribute> getAttributes(Object object)throws IllegalArgumentException;Collection<ConfigAttribute> getAllConfigAttributes();boolean supports(Class<?> clazz);
復制代碼

我們還可以自定義SecurityMetadataSource數據源,實現接口FilterInvocationSecurityMetadataSource。例:

public class MyFilterSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {public List<ConfigAttribute> getAttributes(Object object) {FilterInvocation fi = (FilterInvocation) object;String url = fi.getRequestUrl();String httpMethod = fi.getRequest().getMethod();List<ConfigAttribute> attributes = new ArrayList<ConfigAttribute>();// Lookup your database (or other source) using this information and populate the// list of attributesreturn attributes;}public Collection<ConfigAttribute> getAllConfigAttributes() {return null;}public boolean supports(Class<?> clazz) {return FilterInvocation.class.isAssignableFrom(clazz);}
}
復制代碼

3.4 PasswordEncoder

??為了存儲安全,一般要對密碼進行算法加密,而spring security提供了加密PasswordEncoder接口。其實現類有使用BCrypt hash算法實現的BCryptPasswordEncoder,SCrypt hashing 算法實現的SCryptPasswordEncoder實現類,實現類內部實現可看源碼分析。而PasswordEncoder接口只有兩個方法:

public interface PasswordEncoder {//密碼加密String encode(CharSequence rawPassword);//密碼配對boolean matches(CharSequence rawPassword, String encodedPassword);
} 
復制代碼

4 核心 Security 過濾器(Core Security Filters)

4.1 FilterSecurityInterceptor

??FilterSecurityInterceptor是Spring security授權模塊入口,該類根據訪問的用戶的角色,權限授權訪問那些資源(訪問特定路徑應該具備的權限)。
??FilterSecurityInterceptor封裝FilterInvocation對象進行操作,所有的請求到了這一個filter,如果這個filter之前沒有執行過的話,那么首先執行其父類AbstractSecurityInterceptor提供的InterceptorStatusToken token = super.beforeInvocation(fi),在此方法中使用AuthenticationManager獲取Authentication中用戶詳情,使用ConfigAttribute封裝已定義好訪問權限詳情,并使用AccessDecisionManager.decide()方法進行訪問權限控制。
FilterSecurityInterceptor源碼分析:

public void invoke(FilterInvocation fi) throws IOException, ServletException {if ((fi.getRequest() != null)&& (fi.getRequest().getAttribute(FILTER_APPLIED) != null)&& observeOncePerRequest) {fi.getChain().doFilter(fi.getRequest(), fi.getResponse());}else {// first time this request being called, so perform security checkingif (fi.getRequest() != null && observeOncePerRequest) {fi.getRequest().setAttribute(FILTER_APPLIED, Boolean.TRUE);}//回調其繼承的抽象類AbstractSecurityInterceptor的方法InterceptorStatusToken token = super.beforeInvocation(fi);try {fi.getChain().doFilter(fi.getRequest(), fi.getResponse());}finally {super.finallyInvocation(token);}super.afterInvocation(token, null);}
}
復制代碼

AbstractSecurityInterceptor源碼分析:

protected InterceptorStatusToken beforeInvocation(Object object) {....//獲取所有訪問權限(url-role)屬性列表(已定義在數據庫或者其他地方)Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource().getAttributes(object);....//獲取該用戶訪問信息(包括url,訪問權限)Authentication authenticated = authenticateIfRequired();// Attempt authorizationtry {//進行授權訪問this.accessDecisionManager.decide(authenticated, object, attributes);}catch....
}
復制代碼

4.2 UsernamePasswordAuthenticationFilter

??UsernamePasswordAuthenticationFilter使用username和password表單登錄使用的過濾器,也是最為常用的過濾器。其源碼:

public Authentication attemptAuthentication(HttpServletRequest request,HttpServletResponse response) throws AuthenticationException {//獲取表單中的用戶名和密碼String username = obtainUsername(request);String password = obtainPassword(request);...username = username.trim();//組裝成username+password形式的tokenUsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);// Allow subclasses to set the "details" propertysetDetails(request, authRequest);//交給內部的AuthenticationManager去認證,并返回認證信息return this.getAuthenticationManager().authenticate(authRequest);
}   
復制代碼

??其主要代碼為創建UsernamePasswordAuthenticationToken的Authentication實體以及調用AuthenticationManager進行authenticate認證,根據認證結果執行successfulAuthentication或者unsuccessfulAuthentication,無論成功失敗,一般的實現都是轉發或者重定向等處理,不再細究AuthenticationSuccessHandler和AuthenticationFailureHandle。興趣的可以研究一下其父類AbstractAuthenticationProcessingFilter過濾器。

4.3 AnonymousAuthenticationFilter

AnonymousAuthenticationFilter是匿名登錄過濾器,它位于常用的身份認證過濾器(如UsernamePasswordAuthenticationFilter、BasicAuthenticationFilter、RememberMeAuthenticationFilter)之后,意味著只有在上述身份過濾器執行完畢后,SecurityContext依舊沒有用戶信息,AnonymousAuthenticationFilter該過濾器才會有意義——基于用戶一個匿名身份。 AnonymousAuthenticationFilter源碼分析:

public class AnonymousAuthenticationFilter extends GenericFilterBean implementsInitializingBean {...public AnonymousAuthenticationFilter(String key) {this(key, "anonymousUser", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS"));}...public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)throws IOException, ServletException {if (SecurityContextHolder.getContext().getAuthentication() == null) {//創建匿名登錄Authentication的信息SecurityContextHolder.getContext().setAuthentication(createAuthentication((HttpServletRequest) req));...}chain.doFilter(req, res);}//創建匿名登錄Authentication的信息方法protected Authentication createAuthentication(HttpServletRequest request) {AnonymousAuthenticationToken auth = new AnonymousAuthenticationToken(key,principal, authorities);auth.setDetails(authenticationDetailsSource.buildDetails(request));return auth;}
}
復制代碼

4.4 SecurityContextPersistenceFilter

??SecurityContextPersistenceFilter的兩個主要作用便是request來臨時,創建SecurityContext安全上下文信息和request結束時清空SecurityContextHolder。源碼后續分析。

小節總結:

. AbstractAuthenticationProcessingFilter:主要處理登錄
. FilterSecurityInterceptor:主要處理鑒權

總結

??經過上面對核心的Component、Service、Filter分析,初步了解了Spring Security工作原理以及認證和授權工作流程。Spring Security認證和授權還有很多負責的過程需要深入了解,所以下次會對認證模塊和授權模塊進行更具體工作流程分析以及案例呈現。最后以上純粹個人結合博客和官方文檔總結,如有錯請指出!

轉載于:https://juejin.im/post/5d074dc1f265da1bce3dd10f

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

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

相關文章

windows下手動安裝composer

1.下載compser.phar 地址 https://getcomposer.org/download/ 2.新建composer.bat 文件&#xff0c;寫入“php "%~dp0composer.phar" %*” 3.把composer.bat composer.phar 兩個文件放入 4.向環境變量里面寫人“;D:\phpStudy\php\php-5.4.45;D:\phpStudy\php\php-5…

寫更漂亮的javascript

用更合理的方式寫 JavaScript 目錄 聲明變量對象數組字符串函數箭頭函數模塊迭代器和生成器屬性變量提升比較運算符和等號代碼塊注釋空白逗號分號類型轉換命名規則聲明變量 1.1 使用let和const代替var 不會變的聲明用const//bad var $cat $(.cat)//good const $cat $(.cat)…

筆試小結---樹

平衡二叉樹(Balanced Binary Tree):又被稱為AVL樹,且具有以下性質:它是一棵空樹或它的左右兩個子樹的高度差的絕對值不超過1&#xff0c;并且左右兩個子樹都是一棵平衡二叉樹。 二叉搜索樹&#xff1a;是一顆二叉樹&#xff0c;可能為空;若非空,則滿足以下特征: 1.每個元素有一…

iOS 快速實現分頁界面的搭建

級別&#xff1a; ★★☆☆☆ 標簽&#xff1a;「iOS」「分頁」「QiPageMenuView」 作者&#xff1a; 沐靈洛 審校&#xff1a; QiShare團隊 iOS 快速實現分頁界面的搭建 項目中我們經常會遇到滾動分頁的設計效果&#xff0c;被用來對不同數據界面的展示進行分類。我們先可以來…

java中String的常用方法

java中String的常用方法 轉自&#xff1a;http://archer-zhou.iteye.com/blog/443864 java中String的常用方法1、length() 字符串的長度例&#xff1a;char chars[]{a,b.c};String snew String(chars);int lens.length();2、charAt() 截取一個字符例&#xff1a;char ch;ch&quo…

筆試小結---非對稱加密算法

非對稱加密算法需要兩個密鑰:公開密鑰(publickey)和私有密鑰(privatekey).公開密鑰和私有密鑰是一對,如果公開密鑰對數據進行加密,只有用對應的私有密鑰才能解密;如果用私有密鑰進行加密,那么只有用對應的公開密鑰才能解密. 非對稱加密算法的保密性比較好,它消除了最終用戶交換…

登錄令牌 Token 介紹

Token值介紹 token 值: 登錄令牌.利用 token 值來判斷用戶的登錄狀態.類似于 MD5 加密之后的長字符串. 用戶登錄成功之后,在后端(服務器端)會根據用戶信息生成一個唯一的值.這個值就是 token 值. 基本使用: 在服務器端(數據庫)會保存這個 token 值,以后利用這個 token 值來檢索…

java-number

通常&#xff0c;當我使用number類型的時候&#xff0c;我們可以使用原始數據類型例如byte&#xff0c;int,long,double等 int i 5000; float b 13.65; double m 0xaf; 所有包裝類&#xff08;整型&#xff0c;長型&#xff0c;字節型&#xff0c;雙精度型&#xff0c;浮點型&a…

您的瀏覽器沒有獲得Java Virtual Machine(JVM)支持。可能由于沒有安裝JVM或者已安裝但是沒有啟用。請安裝JVM1.5或者以上版本,如果已安裝則啟用它。...

您的瀏覽器沒有獲得Java Virtual Machine(JVM)支持。可能由于沒有安裝JVM或者已安裝但是沒有啟用。請安裝JVM1.5或者以上版本&#xff0c;如果已安裝則啟用它。 https://www.java.com/zh_CN/download/faq/remove_olderversions.xml https://jingyan.baidu.com/article/6d704a13…

指令定義

restict&#xff1a;它告訴AngularJS這個指令在DOM中可以以何種形式被聲明。 E(元素&#xff09; <my-directive> </mydirective>A(屬性) <div my-directive“expression”> </div>C(類名) <div class“my-directive:expression;”> </div>…

MyBatis學習總結(9)——使用MyBatis Generator自動創建代碼

2019獨角獸企業重金招聘Python工程師標準>>> 由于MyBatis屬于一種半自動的ORM框架&#xff0c;所以主要的工作就是配置Mapping映射文件&#xff0c;但是由于手寫映射文件很容易出錯&#xff0c;所以可利用MyBatis生成器自動生成實體類、DAO接口和Mapping映射文件。這…

[BZOJ2125]最短路(圓方樹DP)

題意&#xff1a;仙人掌圖最短路。 算法&#xff1a;圓方樹DP&#xff0c;$O(n\log nQ\log n)$ 首先建出仙人掌圓方樹&#xff08;與點雙圓方樹的區別在于直接連割邊&#xff0c;也就是存在圓圓邊&#xff09;&#xff0c;然后考慮點u-v的最短路徑&#xff0c;顯然就是&#xf…

20162317 2017-2018-1 《程序設計與數據結構》第8周學習總結

20162317 2017-2018-1 《程序設計與數據結構》第8周學習總結 教材學習內容總結 1、二叉查找樹的定義、性質2、向二叉查找樹中添加元素的方法3、在二叉查找樹中刪除元素的方法4、旋轉的定義、方法、意義 教材學習中的問題和解決過程問題1&#xff1a;我在17章中看到這么一句話&a…

ES6模塊的轉碼

瀏覽器目前還不支持ES6模塊,為了實現立刻使用,我們可以將其轉為ES5的寫法.除了Babel可以用來轉碼,還有以下兩個方法也可以用來轉碼: ES6 moudule transpilerSystemJS ES6 moudule transpiler是square公司開源的一個轉碼器,可以將ES6模塊轉為CommonJS模塊或AMD模塊,從而在瀏覽器…

Java基礎學習總結(22)——異常處理

2019獨角獸企業重金招聘Python工程師標準>>> 一、異常的概念 異常指的是運行期出現的錯誤&#xff0c;也就是當程序開始執行以后執行期出現的錯誤。出現錯誤時觀察錯誤的名字和行號最為重要。 1 package cn.javastudy.summary;2 3 public class TestEx{4 5 …

XAML中格式化日期

要求被格式化數據的類型是DateTime StringFormatyyyy-MM-dd StringFormat{}{0:yyyy-MM-dd}轉載于:https://www.cnblogs.com/changbaishan/p/9144584.html

130242014045 林承暉 第2次實驗

軟件體系結構的第二次實驗&#xff08;解釋器風格與管道過濾器風格&#xff09; 一、實驗目的 1&#xff0e;熟悉體系結構的風格的概念 2&#xff0e;理解和應用管道過濾器型的風格。 3、理解解釋器的原理 4、理解編譯器模型 二、實驗環境 硬件&#xff1a; 軟件&#xff1a;P…

AnularJS1事件

在Web應用的組件是松耦合的情況下&#xff0c;比如需要用戶驗證然后處理授權&#xff0c;即時的通信不總是可行的&#xff0c;因為組件沒有耦合在一起。 例如&#xff0c;如果后端對一個請求返回了狀態碼401&#xff08;表明一個未經授權的請求&#xff09;&#xff0c;我們期望…

Java基礎學習總結(8)——super關鍵字

2019獨角獸企業重金招聘Python工程師標準>>> 一、super關鍵字 在JAVA類中使用super來引用父類的成分&#xff0c;用this來引用當前對象&#xff0c;如果一個類從另外一個類繼承&#xff0c;我們new這個子類的實例對象的時候&#xff0c;這個子類對象里面會有一個父類…

conda鏡像

轉自https://blog.csdn.net/guilutian0541/article/details/81004769 conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main conda config --set show…