SpringSecurity(2)—記住我功能實現

SpringSecurity(2)—記住我功能實現

上一篇博客實現了認證+授權的基本功能,這裡在這個基礎上,添加一個 記住我的功能

上一篇博客地址:SpringSecurity(1)—認證+授權代碼實現

說明:上一遍博客的 用戶數據用戶關聯角色 的信息是在代碼里寫死的,這篇將從mysql數據庫中讀取。

一、數據庫建表

這裏建了三種表

一般權限表有四張或者五張,這裡有關 角色關聯資源表 沒有創建,角色和資源的關係依舊在代碼里寫死。

建表sql

/*創建用戶表*/
CREATE TABLE `persistent_logins` (
  `username` varchar(64) NOT NULL,
  `series` varchar(64) NOT NULL,
  `token` varchar(64) NOT NULL,
  `last_used` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`series`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

/*創建j角色表*/
CREATE TABLE `roles` (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(32) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;

/*創建用戶關聯角色表*/
CREATE TABLE `roles_user` (
  `id` int NOT NULL AUTO_INCREMENT,
  `rid` int DEFAULT '2',
  `uid` int DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=133 DEFAULT CHARSET=utf8;


/*這裏密碼對應的明文 還是123456*/
INSERT INTO `user` (`id`, `username`, `nickname`, `password`, `enabled`)
VALUES
	(1, '小小', '小小', 'e10adc3949ba59abbe56e057f20f883e', 1);

/*三種角色*/
INSERT INTO `roles` (`id`, `name`)
VALUES
	(1, '校長'),
	(2, '教師'),
	(3, '學生');
	
/*小小用戶關聯了 教師和校長角色*/
INSERT INTO `roles_user` (`id`, `rid`, `uid`)
VALUES
	(1, 2, 1),
	(2, 3, 1);

說明:這裏數據庫只有一個用戶

用戶名 :小小

密碼:123456

她所擁有的角色有兩個 教師學生

二、Spring Security的記住我功能基本原理

概念 記住我在登陸的時候都會被用戶勾選,因為它方便地幫助用戶減少了輸入用戶名和密碼的次數,用戶一旦勾選記住我功能那麼 當服務器重啟后依舊可以不用登陸就可以訪問

Spring Security的“記住我”功能的基本原理流程圖如下所示:

這裏大致流程如下:

第一次登陸

用戶請求的時候 remember-me參數為true 時,用戶先進行 認證+授權過濾器。然後走記住我過濾器這裏需要做兩,這裏主要做兩件事。

1.將Token數據存入數據庫 2.將token數據存入cookie中。

服務重啟后

如果服務重啟的話,那麼之前的session信息已經不在了,但是cookie中的Token還是存在的。所以當用戶重啟後去訪問需要認證的接口時,會先通過cookie中的Token

去數據庫查詢這條Token信息,如果存在那麼在通過用戶名去查詢數據庫獲取當前用戶的信息。

三、代碼實現

因為上面項目已經完成了整個授權+認證的過程,那麼這裏就很簡單添加一點點代碼就可以了。

在WebSecurityConfig中添加一個Bean,配置完這個Bean就基本完成了 記住我 功能的開發,然後在將這個Bean設置到configure方法中即可。

    @Bean
    public PersistentTokenRepository tokenRepository() {
        JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
        tokenRepository.setDataSource(dataSource);
        //tokenRepository.setCreateTableOnStartup(true);
        return tokenRepository;
    }

上面的代碼 tokenRepository.setCreateTableOnStartup(true) ;是自動創建Token存到數據庫時候所需要的表,這行代碼只能運行一次,如果重新啟動數據庫,

必須刪除這行代碼,否則將報錯,因為在第一次啟動的時候已經創建了表,不能重複創建。保險起見我們還是註釋掉這段代碼,手動建這張表。

CREATE TABLE `persistent_logins` (
  `username` varchar(64) NOT NULL,
  `series` varchar(64) NOT NULL,
  `token` varchar(64) NOT NULL,
  `last_used` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`series`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

在配置里再加上這些就可以了。

四、測試

主要測試兩個點地方,

1、當我登陸時選擇記住我功能,看下數據庫persistent_logins是否有一條token記錄
2、當使用記住我功能后,關閉服務器在重啟服務器,不再登陸直接訪問需要認證的接口,看是否能夠訪問成功。

1、首次登陸

我們在看數據庫token表

很明顯新增了一條token數據。

2、重啟服務器

這個時候我們重啟服務器訪問需要認證的接口

發現就算重啟也不需要重啟登陸就可以反問需要認證的接口。

五、源碼分析

同樣這裏也分為兩部分 1、第一次登陸源碼流程。 2、重啟后未認證再去訪問需要認證的接口源碼流程。

1、首次登陸源碼流程

第一步

當用戶發送登錄請求的時候,首先到達的是UsernamePasswordAuthenticationFilter這個過濾器,然後執行attemptAuthentication方法的代碼,代碼如下圖所示:

 public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
 //從這裏可以看出登陸需要post提交
        if (this.postOnly && !request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
        } else {
            String username = this.obtainUsername(request);
            String password = this.obtainPassword(request);
            if (username == null) {
                username = "";
            }

            if (password == null) {
                password = "";
            }

            username = username.trim();
            UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
            this.setDetails(request, authRequest);
            return this.getAuthenticationManager().authenticate(authRequest);
        }
    }

之後所走的流程就是 ProviderManager的authenticate方法 ,之後再走AbstractUserDetailsAuthenticationProvider的authenticate方法,再走DaoAuthenticationProvider的方法retrieveUser方法

 protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
        this.prepareTimingAttackProtection();

        try {
            //這裏就走我們自定義的獲取用戶認證和授權信息的代碼了
            UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
            if (loadedUser == null) {
                throw new InternalAuthenticationServiceException("UserDetailsService returned null, which is an interface contract violation");
            } else {
                return loadedUser;
            }
        } catch (UsernameNotFoundException var4) {
            this.mitigateAgainstTimingAttack(authentication);
            throw var4;
        } catch (InternalAuthenticationServiceException var5) {
            throw var5;
        } catch (Exception var6) {
            throw new InternalAuthenticationServiceException(var6.getMessage(), var6);
        }
    }

這樣一來,認證的流程就已經走完了。那就要走記住我功能的過濾器了。

第二步

驗證成功之後,將進入AbstractAuthenticationProcessingFilter 類的successfulAuthentication的方法中,首先將認證信息通過代碼
SecurityContextHolder.getContext().setAuthentication(authResult);將認證信息存入到session中,緊接着這個方法中就調用了rememberMeServices的loginSuccess方法

 protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Authentication success. Updating SecurityContextHolder to contain: " + authResult);
        }

        SecurityContextHolder.getContext().setAuthentication(authResult);
        //記住我
        this.rememberMeServices.loginSuccess(request, response, authResult);
        if (this.eventPublisher != null) {
            this.eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
        }

        this.successHandler.onAuthenticationSuccess(request, response, authResult);
    }

再走PersistentTokenBasedRememberMeServices的onLoginSuccess方法

    protected void onLoginSuccess(HttpServletRequest request, HttpServletResponse response, Authentication successfulAuthentication) {
        String username = successfulAuthentication.getName();
        this.logger.debug("Creating new persistent login for user " + username);
        PersistentRememberMeToken persistentToken = new PersistentRememberMeToken(username, this.generateSeriesData(), this.generateTokenData(), new Date());

        try {
            //這裏就是關鍵的兩步 1、將token存入到數據庫 2、將token存入cookie中
            this.tokenRepository.createNewToken(persistentToken);
            this.addCookie(persistentToken, request, response);
        } catch (Exception var7) {
            this.logger.error("Failed to save persistent token ", var7);
        }

    }

這個方法中調用了tokenRepository來創建Token並存到數據庫中,且將Token寫回到了Cookie中。到這裏,基本的登錄過程基本完成,生成了Token存到了數據庫,

且寫回到了Cookie中。

2、第二次訪問

重啟項目,這時候服務器端的session已經不存在了,但是第一次登錄成功已經將Token寫到了數據庫和Cookie中,直接訪問一個服務,並且不輸入用戶名和密碼。

第一步

首先進入到了RememberMeAuthenticationFilter的doFilter方法中,這個方法首先檢查在session中是否存在已經驗證過的Authentication了,如果為空,就進行下面的

RememberMe的驗證代碼,比如調用rememberMeServices的autoLogin方法,代碼如下:

    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest)req;
        HttpServletResponse response = (HttpServletResponse)res;
        if (SecurityContextHolder.getContext().getAuthentication() == null) {
            //走記住我流程
            Authentication rememberMeAuth = this.rememberMeServices.autoLogin(request, response);
            //省略不重要的代碼
            chain.doFilter(request, response);
        } else {
            chain.doFilter(request, response);
        }
    }

我們在看this.rememberMeServices.autoLogin(request, response)方法。最終實現在AbstractRememberMeServices的autoLogin方法

    public final Authentication autoLogin(HttpServletRequest request, HttpServletResponse response) {
        //1、獲取token
        String rememberMeCookie = this.extractRememberMeCookie(request);
        if (rememberMeCookie == null) {
            return null;
        } else {
    
                UserDetails user = null;
                try {
                    String[] cookieTokens = this.decodeCookie(rememberMeCookie);
                    //這步是關鍵
                    user = this.processAutoLoginCookie(cookieTokens, request, response);
                    this.userDetailsChecker.check(user);
                    this.logger.debug("Remember-me cookie accepted");
                    return this.createSuccessfulAuthentication(request, user);
                } catch (CookieTheftException var6) {
                    this.cancelCookie(request, response);
                    throw var6;
                } 
                this.cancelCookie(request, response);
                return null;
            }
        }
    }

我們在看 this.processAutoLoginCookie(cookieTokens, request, response);在PersistentTokenBasedRememberMeServices中實現,到這一步就已經很明白了

 protected UserDetails processAutoLoginCookie(String[] cookieTokens, HttpServletRequest request, HttpServletResponse response) {
        if (cookieTokens.length != 2) {
            throw new InvalidCookieException("Cookie token did not contain 2 tokens, but contained '" + Arrays.asList(cookieTokens) + "'");
        } else {
            String presentedSeries = cookieTokens[0];
            String presentedToken = cookieTokens[1];
            //1、去token表中查詢token
            PersistentRememberMeToken token = this.tokenRepository.getTokenForSeries(presentedSeries);
            if (token == null) {
                throw new RememberMeAuthenticationException("No persistent token found for series id: " + presentedSeries);
                //2校驗數據
            } else if (!presentedToken.equals(token.getTokenValue())) {
                this.tokenRepository.removeUserTokens(token.getUsername());
                throw new CookieTheftException(this.messages.getMessage("PersistentTokenBasedRememberMeServices.cookieStolen", "Invalid remember-me token (Series/token) mismatch. Implies previous cookie theft attack."));
                //3、查看token是否過期
            } else if (token.getDate().getTime() + (long)this.getTokenValiditySeconds() * 1000L < System.currentTimeMillis()) {
                throw new RememberMeAuthenticationException("Remember-me login has expired");
            } else {
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Refreshing persistent login token for user '" + token.getUsername() + "', series '" + token.getSeries() + "'");
                }

                PersistentRememberMeToken newToken = new PersistentRememberMeToken(token.getUsername(), token.getSeries(), this.generateTokenData(), new Date());

                try {
                //4、更新這條token 沒更新一次有效時間就都變成了之間設置的時間
                    this.tokenRepository.updateToken(newToken.getSeries(), newToken.getTokenValue(), newToken.getDate());
                    this.addCookie(newToken, request, response);
                } catch (Exception var9) {
                    this.logger.error("Failed to update token: ", var9);
                    throw new RememberMeAuthenticationException("Autologin failed due to data access problem");
                }
                 //5、這裏拿着用戶名 就又獲取當前用戶的認證和授權信息
                return this.getUserDetailsService().loadUserByUsername(token.getUsername());
            }
        }
    }

這樣整個流程就完成了,我們可以看出源碼的過程和上面圖片展示的流程還是非常像的。

別人罵我胖,我會生氣,因為我心裏承認了我胖。別人說我矮,我就會覺得好笑,因為我心裏知道我不可能矮。這就是我們為什麼會對別人的攻擊生氣。
攻我盾者,乃我內心之矛(18)

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計最專業,超強功能平台可客製化

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※回頭車貨運收費標準

※推薦評價好的iphone維修中心

※教你寫出一流的銷售文案?

台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!

台中搬家公司費用怎麼算?

Shell中傻傻分不清楚的TOP3

Shell中傻傻分不清楚的TOP3
發布文章

近來小姐姐又犯憨憨錯誤,問組內小夥伴export命令不會持久化環境變量嗎?反正我是問出口了。。然後小夥伴就甩給了我一個《The Linux Command Line》PDF鏈接。感謝老大不殺之恩~

Shell是命令解釋器,它會接受用戶輸入的各種命令,並傳遞給操作系統執行。它的作用類似於Windows系統的命令行。在UNIX或Linux系統中,Shell即是用戶交互的界面,也是控制系統的腳本語言。當然現在用戶也可以選擇圖形化界面做一些和操作系統的交互。層次示意圖如下:

對於初學者來說,可能搞不清楚Shell怎麼會有那麼多分類,Shell的語法怎麼那麼隨便…

小姐姐結合自己初學Shell傻傻分不清的問題點,主要從Shell的種類,變量的分類,條件測試的表達三個部分來介紹。

Shell的種類

shell程序有shbash,zsh等分類,我從網上找到一張圖可以看出shell程序的發展史。

對於這些Shell程序,其語法或多或少有一些差異,不過我們通常使用的都是bash。

  • Shell程序信息

在Linux系統我們可以通過一些命令查看或修改當前Shell程序信息。

一般發行版的Linux系統中,默認的shell程序就是bash。我們在寫shell腳本時,通常也會在腳本文件頭部指定bash作為腳本解釋器。

這裏多說一句,zsh有時也作為猿媛們的默認shell。zsh語法大多是和bash匹配的,也不會影響shell腳本的執行(因為腳本頭部指定bash就還是bash:),也不會影響像小姐姐這樣的渣渣使用。用它是因為它有神奇的開源框架 Oh My God.. 哦不,是 Oh My Zsh !!!

後面的內容我們還是以Linux系統中的bash為例來介紹:)

變量的分類

Shell是一門動態類型語言和弱類型語言,我們可以把變量理解為KV對,key是變量名,value是變量值。變量大體可以分為環境變量系統變量用戶定義的變量三類。

  • 環境變量

比如我們經常配置的JAVA_HOME就屬於環境變量,這些變量是所有Shell程序運行時都可以使用的變量。關於環境變量的操作命令舉例如下:

使用export命令定義的環境變量只在當前運行的shell進程中有效,結束進程就沒了。所以我們要將配置變量定義在令小姐姐懵逼的一系列配置文件中,持久化下來。

說起配置文件,又不得不先提下shell程序和用戶的Interactive和Login模式:)

  • Interactive & Non-Interactive`

Interactive通常是指讀入寫出數據都是從用戶的terminal,也就是我們平時用命令行打開終端就是Interactive模式,而執行一個shell腳本就是Non-interactive模式。怎麼檢驗當前shell運行的模式是不是Interactive呢?小姐姐從GNU網站拷貝了一段裝X腳本:

case "$-" in
*i*)	echo This shell is interactive ;;
*)	echo This shell is not interactive ;;
esac

結果如上所述。

  • Login & Non-Login`

Login模式指的是用戶成功登錄后開啟的shell進程,這時候會讀取/etc/passwd下用戶所屬的shell去執行。

Non-login模式指的是非登錄用戶狀態下開啟的shell進程,我們可以通過echo $0區分。

扯這麼多是因為配置文件的加載順序和shell進程是否運行在Interactive和Login模式有關係:D

這是阿姨從網上粘的圖。bash支持的配置文件有/etc/profile,~/.bashrc等。

當調用一個Interactive&Login模式的shell進程時,配置文件的加載順序為:

/etc/profile —>( ~/.bash_profile, ~/.bash_login, ~/.profile)其中之一 —>~/.bash_loginout(退出shell時調用)

當調用一個Interactive&non-Login模式的shell進程時,配置文件的加載順序為:

/etc/bash.bashrc —> ~/.bashrc
當調用一個non-nteractive模式的shell進程時,通常是執行腳本時,此時配置項是從環境變量中讀取和執行的,也就是env命令輸出的配置項。

另外,在開啟一個shell進程中,有一些參數的值也會影響到配置文件的加載。如–rcfile ,–norc等。這些參數的含義值可以使用 man bash進一步了解。只要保持默認值,其實就是我們上面介紹的配置文件加載順序。

還有,在發行版的Linux系統中,Interactive&Login模式下的~/.bash_profile,
~/.bash_login, ~/.profile並不一定是三選一,看一下這三個腳本的內容會發現他們會繼續調用下一個它想調用的配置文件,這樣就可以避免配置項可能需要在不同的配置文件多次配置的弊端了。如centos7.2 中 ~/.bash_profile文件中實際調用了 ~/.bashrc文件。

按照模式和參數設置啟動的shell程序的配置文件加載流程圖如下:

好了,到目前我們總算把環境變量中配置文件的加載順序理清了。下面列舉一些常用的Shell環境變量吧。

  • 系統變量
    Shell中系統變量主要在對參數判斷和命令返回值判斷時使用,包括腳本和函數的參數和返回值判斷。沒啥可說的,主要難記且腳本中經常出現:

  • 用戶自定義的變量
    是指我們在使用命令或腳本時定義的變量,因為shell是弱類型語言且語法XX,這裏主要談談初學時的幾個坑爹點:

  • “=” 左右兩邊不能有空格

    你懂我說的意思了吧。。

  • 引用
    所謂引用,指的是將字符串用引用符號包括起來,以防止其中的特殊符號被Shell解釋為其他涵義。

常用的引用符號如下:

  • $

前面我們其實一直在用 “ $變量名 ” 來表示某個 變量名的值,這其實也正是 $ 的作用。

  • shell中變量名的大小寫是敏感的

好了就這麼多吧。

條件測試的表達

shell腳本中除了變量,還經常出現的語法就是條件測試的判斷。不會寫腳本的開發小姐姐不是好運維,我們來一起侃侃吧。

  • 基本語法

在Shell程序中,當指定的條件為真時,整個條件測試的返回值為 0;反之,如果指定的條件為假時,整個條件測試的返回值為 非0。

條件測試表達式的書寫有test expression[ expression ]兩種形式,注意後者的空格一定不能省!!

腳本中經常出現的有字符串測試、整數測試、文件測試、邏輯操作符測試。我們一起看下:)

  • 字符串測試

注意:這裏運算符 左右兩邊又一定要有空格了(下同),這樣shell才能將之當成命令執行。

  • 整數測試

  • 文件測試

邏輯操作符測試

收藏在看轉發起來,小姐姐就算你條件測試過關了>_<

感謝大佬這麼優秀還關注我~

參考資料:
[1].《Shell從入門到精通》
[2].https://www.edureka.co/blog/types-of-shells-in-linux/
[3].http://www.penguintutor.com/linux/basic-shell-reference
[4].https://apple.stackexchange.com/questions/361870/what-are-the-practical-differences-between-bash-and-zsh
[5].https://sunlightmedia.org/bash-vs-zsh/
[6].https://unix.stackexchange.com/questions/439042/debian-read-order-of-bash-session-configuration-files-inconsistent
[7].https://www.gnu.org/software/bash/manual/html_node/Bash-Startup-Files.html
[8].http://howtolamp.com/articles/difference-between-login-and-non-login-shell/
[9].https://shreevatsa.wordpress.com/2008/03/30/zshbash-startup-files-loading-order-bashrc-zshrc-etc/

Shell中傻傻分不清楚的TOP3
同步滾動:

近來小姐姐又犯憨憨錯誤,問組內小夥伴export命令不會持久化環境變量嗎?反正我是問出口了。。然後小夥伴就甩給了我一個《The Linux Command Line》PDF鏈接。感謝老大不殺之恩~

Shell是命令解釋器,它會接受用戶輸入的各種命令,並傳遞給操作系統執行。它的作用類似於Windows系統的命令行。在UNIX或Linux系統中,Shell即是用戶交互的界面,也是控制系統的腳本語言。當然現在用戶也可以選擇圖形化界面做一些和操作系統的交互。層次示意圖如下:

對於初學者來說,可能搞不清楚Shell怎麼會有那麼多分類,Shell的語法怎麼那麼隨便…

小姐姐結合自己初學Shell傻傻分不清的問題點,主要從Shell的種類,變量的分類,條件測試的表達三個部分來介紹。

Shell的種類
shell程序有sh,bash,zsh等分類,我從網上找到一張圖可以看出shell程序的發展史。

對於這些Shell程序,其語法或多或少有一些差異,不過我們通常使用的都是bash。
Shell程序信息
在Linux系統我們可以通過一些命令查看或修改當前Shell程序信息。

一般發行版的Linux系統中,默認的shell程序就是bash。我們在寫shell腳本時,通常也會在腳本文件頭部指定bash作為腳本解釋器。

這裏多說一句,zsh有時也作為猿媛們的默認shell。zsh語法大多是和bash匹配的,也不會影響shell腳本的執行(因為腳本頭部指定bash就還是bash:),也不會影響像小姐姐這樣的渣渣使用。用它是因為它有神奇的開源框架 Oh My God.. 哦不,是 Oh My Zsh !!!

後面的內容我們還是以Linux系統中的bash為例來介紹:)

變量的分類
Shell是一門動態類型語言和弱類型語言,我們可以把變量理解為KV對,key是變量名,value是變量值。變量大體可以分為環境變量,系統變量,用戶定義的變量三類。

環境變量
比如我們經常配置的JAVA_HOME就屬於環境變量,這些變量是所有Shell程序運行時都可以使用的變量。關於環境變量的操作命令舉例如下:

使用export命令定義的環境變量只在當前運行的shell進程中有效,結束進程就沒了。所以我們要將配置變量定義在令小姐姐懵逼的一系列配置文件中,持久化下來。

說起配置文件,又不得不先提下shell程序和用戶的Interactive和Login模式:)

Interactive & Non-Interactive`
Interactive通常是指讀入寫出數據都是從用戶的terminal,也就是我們平時用命令行打開終端就是Interactive模式,而執行一個shell腳本就是Non-interactive模式。怎麼檢驗當前shell運行的模式是不是Interactive呢?小姐姐從GNU網站拷貝了一段裝X腳本:

case “$-” in
i) echo This shell is interactive ;;
*) echo This shell is not interactive ;;
esac
結果如上所述。

Login & Non-Login`
Login模式指的是用戶成功登錄后開啟的shell進程,這時候會讀取/etc/passwd下用戶所屬的shell去執行。

Non-login模式指的是非登錄用戶狀態下開啟的shell進程,我們可以通過echo $0區分。

扯這麼多是因為配置文件的加載順序和shell進程是否運行在Interactive和Login模式有關係:D

這是阿姨從網上粘的圖。bash支持的配置文件有/etc/profile,~/.bashrc等。

當調用一個Interactive&Login模式的shell進程時,配置文件的加載順序為:

/etc/profile —>( ~/.bash_profile, ~/.bash_login, ~/.profile)其中之一 —>~/.bash_loginout(退出shell時調用)

當調用一個Interactive&non-Login模式的shell進程時,配置文件的加載順序為:

/etc/bash.bashrc —>~/.bashrc
當調用一個non-nteractive模式的shell進程時,通常是執行腳本時,此時配置項是從環境變量中讀取和執行的,也就是env命令輸出的配置項。

另外,在開啟一個shell進程中,有一些參數的值也會影響到配置文件的加載。如–rcfile ,–norc等。這些參數的含義值可以使用man bash進一步了解。只要保持默認值,其實就是我們上面介紹的配置文件加載順序。

還有,在發行版的Linux系統中,Interactive&Login模式下的~/.bash_profile,
~/.bash_login, ~/.profile並不一定是三選一,看一下這三個腳本的內容會發現他們會繼續調用下一個它想調用的配置文件,這樣就可以避免配置項可能需要在不同的配置文件多次配置的弊端了。如centos7.2 中 ~/.bash_profile文件中實際調用了 ~/.bashrc文件。

按照模式和參數設置啟動的shell程序的配置文件加載流程圖如下:

好了,到目前我們總算把環境變量中配置文件的加載順序理清了。下面列舉一些常用的Shell環境變量吧。

系統變量
Shell中系統變量主要在對參數判斷和命令返回值判斷時使用,包括腳本和函數的參數和返回值判斷。沒啥可說的,主要難記且腳本中經常出現:

用戶自定義的變量
是指我們在使用命令或腳本時定義的變量,因為shell是弱類型語言且語法XX,這裏主要談談初學時的幾個坑爹點:

“=” 左右兩邊不能有空格

你懂我說的意思了吧。。

引用
所謂引用,指的是將字符串用引用符號包括起來,以防止其中的特殊符號被Shell解釋為其他涵義。

常用的引用符號如下:

$
前面我們其實一直在用 “ 變量名 ” 來表示某個 變量名的值,這其實也正是 的作用。

shell中變量名的大小寫是敏感的
好了就這麼多吧。

條件測試的表達
shell腳本中除了變量,還經常出現的語法就是條件測試的判斷。不會寫腳本的開發小姐姐不是好運維,我們來一起侃侃吧。

基本語法
在Shell程序中,當指定的條件為真時,整個條件測試的返回值為 0;反之,如果指定的條件為假時,整個條件測試的返回值為 非0。

條件測試表達式的書寫有test expression 和 [ expression ]兩種形式,注意後者的空格一定不能省!!

腳本中經常出現的有字符串測試、整數測試、文件測試、邏輯操作符測試。我們一起看下:)

字符串測試

注意:這裏運算符 左右兩邊又一定要有空格了(下同),這樣shell才能將之當成命令執行。

整數測試

文件測試

邏輯操作符測試

收藏在看轉發起來,小姐姐就算你條件測試過關了>_<

感謝大佬這麼優秀還關注我~

參考資料:
[1].《Shell從入門到精通》
[2].https://www.edureka.co/blog/types-of-shells-in-linux/
[3].http://www.penguintutor.com/linux/basic-shell-reference
[4].https://apple.stackexchange.com/questions/361870/what-are-the-practical-differences-between-bash-and-zsh
[5].https://sunlightmedia.org/bash-vs-zsh/
[6].https://unix.stackexchange.com/questions/439042/debian-read-order-of-bash-session-configuration-files-inconsistent
[7].https://www.gnu.org/software/bash/manual/html_node/Bash-Startup-Files.html
[8].http://howtolamp.com/articles/difference-between-login-and-non-login-shell/
[9].https://shreevatsa.wordpress.com/2008/03/30/zshbash-startup-files-loading-order-bashrc-zshrc-etc/

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※回頭車貨運收費標準

※推薦評價好的iphone維修中心

※超省錢租車方案

台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

※推薦台中搬家公司優質服務,可到府估價

Hive的基本介紹以及常用函數

一、Hive的簡介:

  Hive是基於Hadoop的一個數據倉庫工具,可以將結構化的數據文件映射為一張表,並提供類SQL查詢功能。

優點:

1) 操作接口採用類SQL語法,提供快速開發的能力(簡單、容易上手)。

2) 避免了去寫MapReduce,減少開發人員的學習成本。

3) Hive的執行延遲比較高,因此Hive常用於數據分析,對實時性要求不高的場合。

4) Hive優勢在於處理大數據,對於處理小數據沒有優勢,因為Hive的執行延遲比較高。

5) Hive支持用戶自定義函數,用戶可以根據自己的需求來實現自己的函數。

缺點:

1)Hive的HQL表達能力有限
2)Hive的效率比較低

 

二、Hive的架構圖

          

三、Hive的數據類型

基本數據類型:
  Hive數據類型    Java數據類型 
  TINYINT       byte    
  SMALINT       short    
  INT            int    
  BIGINT       long    
  BOOLEAN       boolean  
  FLOAT        float    
  DOUBLE        double    
  STRING         string   
  TIMESTAMP      時間類型    
  BINARY         字節數組    
集合
  STRUCT  和c語言中的struct類似,都可以通過“點”符號訪問元素內容。例如,如果某個列的數據類型是STRUCT{first STRING, last STRING},那麼第1個元素可以通過字段.first來引用。    
      struct() 例如struct<street:string, city:string>   MAP MAP是一組鍵-值對元組集合,使用數組表示法可以訪問數據。例如,如果某個列的數據類型是MAP,其中鍵->值對是’first’->’John’和’last’->’Doe’,那麼可以通過字段名[‘last’]獲取最後一個元素
      map() 例如map
<string, int>   ARRAY 數組是一組具有相同類型和名稱的變量的集合。這些變量稱為數組的元素,每個數組元素都有一個編號,編號從零開始。例如,數組值為[‘John’, ‘Doe’],那麼第2個元素可以通過數組名[1]進行引用。
      Array() 例如array
<string>

 

四、管理表和外部表

  默認創建的表為管理表,有時也被稱為內部表。當我們刪除一個管理表時,Hive也會刪除這個表中數據。管理表不適合和其他工具共享數據。  

  添加關鍵字external創建的表是外部表,所以Hive並非認為其完全擁有這份數據。刪除該表並不會刪除掉這份數據,不過描述表的元數據信息會被刪除掉。

#查詢表的類型
desc formatted stu;
#修改內部表stu為外部表
alter table stuset tblproperties('EXTERNAL'='TRUE');
#修改外部表stu為內部表
alter table stuset tblproperties('EXTERNAL'='FALSE');
注意:('EXTERNAL'='TRUE')和('EXTERNAL'='FALSE')為固定寫法,區分大小寫!

清空表數據:

 管理表:truncate table stu;

 外部表:alter table stuset tblproperties(‘EXTERNAL’=’FALSE’); 先改成內部表再執行清空:truncate table stu;

  場景:每天將收集到的網站日誌定期流入HDFS文本文件。在外部表(原始日誌表)的基礎上做大量的統計分析,用到的中間表、結果表使用內部表存儲,數據通過SELECT+INSERT進入內部表。

 

五、查詢常用函數

1,空字段賦值

  NVL( value,default_value)

2,when

  case 字段 when 值 then 替換值 else 替換值 end 同 if(boolean,值,另外值)

3,行轉列

CONCAT(string A/col, string B/col…):返回輸入字符串連接后的結果,支持任意個輸入字符串; 
CONCAT_WS(separator, str1, str2,...):它是一個特殊形式的 CONCAT()。第一個參數剩餘參數間的分隔符。分隔符可以是與剩餘參數一樣的字符串。如果分隔符是 NULL,返回值也將為 NULL。這個函數會跳過分隔符參數后的任何 NULL 和空字符串。分隔符將被加到被連接的字符串之間;
COLLECT_SET(col):函數只接受基本數據類型,它的主要作用是將某字段的值進行去重匯總,產生array類型字段。

4,列轉行

EXPLODE(col):將hive一列中複雜的array或者map結構拆分成多行。
LATERAL VIEW
用法:LATERAL VIEW udtf(expression) tableAlias AS columnAlias
解釋:用於和split, explode等UDTF一起使用,它能夠將一列數據拆成多行數據,在此基礎上可以對拆分后的數據進行聚合。

5,窗口函數

OVER():指定分析函數工作的數據窗口大小,這個數據窗口大小可能會隨着行的變而變化。
CURRENT ROW:當前行
n PRECEDING:往前n行數據
n FOLLOWING:往後n行數據
UNBOUNDED:起點,UNBOUNDED PRECEDING 表示從前面的起點, UNBOUNDED FOLLOWING表示到後面的終點
LAG(col,n,default_val):往前第n行數據
LEAD(col,n, default_val):往後第n行數據
NTILE(n):把有序分區中的行分發到指定數據的組中,各個組有編號,編號從1開始,對於每一行,NTILE返回此行所屬的組的編號。注意:n必須為int類型。

6,Rank

RANK() 排序相同時會重複,總數不會變
DENSE_RANK() 排序相同時會重複,總數會減少
ROW_NUMBER() 會根據順序計算

7,時間

#均只適合yyyy-MM-dd HH:mm:ss格式的時間,當然時分秒可以省略
date_format:格式化時間select date_format('2020-06-04','yyyy-MM');2020-06
date_add:   時間跟天數相加select date_add('2020-06-04',5);2020-06-09
date_sub:   時間跟天數相減select date_sub('2020-06-04',5);2020-05-30
datediff:   兩個時間相減select datediff('2020-06-04','2020-05-10');25
year:       獲取年select year('2020-06-04');2020
month:      獲取月份select month('2020-06-04');04
day:        獲取日select day('2020-06-04');04

格式轉換
regexp_replace: 字段,原格式,替換的格式;select regexp_replace('2020/05/04','/','-');2020-05-04

 

 

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※回頭車貨運收費標準

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※推薦評價好的iphone維修中心

※教你寫出一流的銷售文案?

台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!

台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

墨西哥居民死守水壩 不讓政府輸水給美國

摘錄自2020年9月21日中央社報導

群眾今(20日)聚集在乾旱成災的墨西哥北部示威,試圖保住一座位於美墨邊界的水壩控制權。向來致力與美國總統川普維持良好關係的墨西哥總統羅培茲歐布拉多(Andres Manuel Lopez Obrador)18日表示,墨西哥必須善盡義務。

根據1944年的條約,墨西哥必須在5年內把175萬英畝呎(acre-feet)的水輸給美國,墨西哥也會拿到美國科羅拉多河(Colorado River)的水源。

美國德州州長艾波特(Greg Abbott)上週促請國務院協助落實協議。墨西哥還差近一年的水量才能達到條約要求,10月24日前,必須還完近1年量的水債。

但墨西哥民眾拒絕輸水給美國,群眾9月間在與美國新墨西哥州接壤的奇瓦瓦州(Chihuahua)波基亞水壩(La Boquilla dam)附近示威,奪得水壩控制權,但示威期間卻發生濺血事件,一個星期前,一名民眾在示威時遭砲火波及,死在墨西哥國民兵的子彈之下。

墨西哥國家水務委員會(National Water Commission of Mexico)發布的旱情監測顯示,奇瓦瓦州大片地區已有超過6個月出現中度和重度乾旱,鄰州索諾拉(Sonora)與科阿韋拉州(Coahuila)也受乾旱影響。

國際新聞
墨西哥
水壩
輸水
抗議

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※別再煩惱如何寫文案,掌握八大原則!

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※超省錢租車方案

※教你寫出一流的銷售文案?

網頁設計最專業,超強功能平台可客製化

※產品缺大量曝光嗎?你需要的是一流包裝設計!

台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

凍土冰融會將「水銀」注入河水!魚蝦蟹將不能食用

摘錄自2020年9月22日東森新聞報導

美國科學期刊《自然通訊》16日發表一份最新研究指出,若北美阿拉斯加州凍土持續融化,多種有毒物質將流入加拿大「育空河流域」(Yukon River)中,將導致河內生物死亡或不適宜食用。

研究人員發現,永久凍土中因含有大量水銀,預計到2050年當地的魚類水銀含量超過聯邦安全準則,到本世紀末水銀更將增加一倍。

科羅拉多州國家冰雪數據中心謝弗(Kevin Schaefer)表示,育空地區發生的事情不僅影響居住在育空河的人們,更會影響整個地球。凍土融化釋放的水銀將從土地流向海洋,最終環繞世界。

不過,若各國遵守2015年聯合國簽訂的《巴黎協定》,減少碳排放量,預計到本世紀末,水銀比現在僅增加14%,仍符合安全標準。

 

 

氣候變遷
國際新聞
凍土融化
永凍土

汞汙染
海鮮

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計最專業,超強功能平台可客製化

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※回頭車貨運收費標準

※推薦評價好的iphone維修中心

※教你寫出一流的銷售文案?

台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!

台中搬家公司費用怎麼算?

政府串通黑手黨 疫情遮掩下 柬埔寨森林的悲傷敘事

環境資訊中心綜合外電;黃鈺婷 翻譯;林大利 審校;稿源:Mongabay

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※回頭車貨運收費標準

※推薦評價好的iphone維修中心

※超省錢租車方案

台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

※推薦台中搬家公司優質服務,可到府估價

IKEA推廣循環經濟 將在27國回購舊家具

摘錄自2020年10月13日中央社報導

瑞典連鎖家具家飾品零售商宜家家居(IKEA)今(13日)表示,將在27國提供回購數以千計舊家具服務,並進行轉售、回收或捐贈給社區計畫。

美聯社報導,宜家家居今天表示,這個從11月24日開始到12月3日為止的黑色星期五活動,將是「藉由為循環經濟做出貢獻,滿足顧客需求的一個機會」。出售宜家家居舊家具的消費者,將能獲得高達原價50%的折價券,用來購買新品。

循環經濟
國際新聞
IKEA
回收
家具

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※回頭車貨運收費標準

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※推薦評價好的iphone維修中心

※教你寫出一流的銷售文案?

台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!

台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

Redmi K40 / K40 Pro、小米10 新版本外觀認證照曝光

不久前, Redmi K40 終於宣佈將在春節新年後的 2 月 25 日正式發表,近期也有 Redmi K40 系列以及全新處理器更新的小米10 的新機現身中國工信部資料庫提前洩漏外觀照片。 Redmi K40 系列在機身背面設計與之前工程樣機有些不同,尤其主相機設計與小米11 有著相似的設計風格。
另外,傳聞將換上高通 Snapdragon 870 處理器的小米10 系列新機則與小米10 至尊紀念版(小米10 Ultra)設計相似。

▲圖片來源:TENAA

Redmi K40 / K40 Pro、小米10 新版本外觀認證照曝光

除了華為已經確定將在 2 月 22 日發表全新摺疊螢幕手機 Mate X2 ,日前 Redmi K40 也確定在 2 月 25 日發表。這次 Redmi K40 系列價格已確定為人民幣 2,999 元起(約合新台幣 12,912 元起),不過究竟 25 日發表會這天除了 Redmi K40 標準版,是否還會推出 K40 Pro 甚至是 Redmi 品牌首款電競手機,或許也要等到發表當天才能確定。

▲圖片來源:盧偉冰(微博)

不過在這之前, Redmi K40 系列也終於外觀認證照片在有中國工信部曝光。首先是 Redmi K40(型號 M2012K11AC),在螢幕正面採用 120Hz AMOLED 平面的居中挖孔全螢幕,傳聞前置鏡頭開孔口徑僅有 2.8mm ,比目前市面上最小的 2.98mm 還小(預計 2.8mm)。 Redmi K40 主相機模組設計和小米11 有些相似,不過排列方式變得更為「修長」,預計接下來今年 Redmi 未來更多款中階、入門新機也會採用類似的設計和居中挖孔螢幕。

▲圖片來源:TENAA

在機身側邊較大的電源鍵設計,傳聞 Redmi K40 將電源鍵將結合側邊指紋辨識:

▲圖片來源:TENAA

另外一款疑似為 Redmi K40 Pro(型號 M2012K11C),在機身整體的設計幾乎相同,不過在相機規格部分升級為 1.08 億像素主相機:

▲圖片來源:TENAA

另外,傳聞小米接下來除了小米11 系列,還將推出搭載高通 Snapdragon 870 處理器的全新小米10 系列新機,目前機身外觀照片也在中國工信部資料庫曝光。這款改版的小米10 系列新機目前還未確定名稱,不過螢幕採用左上角單挖孔的曲面螢幕,機身背面主相機設計和小米10 至尊紀念版(小米10 Ultra)相同。

▲圖片來源:TENAA

延伸閱讀:
小米MIX系列「可量產」新機確認將在今年即將回歸,另有小米平板也將登場

小米11 國際版正式發表:盒裝標配 55W GaN 氮化鎵充電器、售價約 25,150 元

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※想知道最厲害的網頁設計公司"嚨底家"!

※別再煩惱如何寫文案,掌握八大原則!

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※回頭車貨運收費標準

台中搬家公司費用怎麼算?

好車不一定貴,很多人的夢想座駕其實只要十來萬!

8L雙VVT-i机械增壓發動機,最大功率僅為220馬力,你肯定會覺得在開玩笑,其實Elise S整車重量只有924kg,其推重比為238馬力/噸,保時捷911 2016款 Carrera 3。0T的推重比也僅僅是243馬力/噸,可想而知Elise S擁有着怎樣的性能。百公里加速4。

誕生於1950年的大眾T1,是大眾汽車歷史上的第二款車型,造型就像是一個盒子,只不過是圓潤點的盒子,大大的logo擺放在車頭正中央,加上圓鼓鼓的車燈,非常可愛,加上V型的分割線條,像是什麼東西大家就自行腦補吧。

把大眾T1稱為“駕駛機器”一點都不過分,駕駛室全是裸露的金屬,剩下的就是方向盤、換擋桿和油門、剎車、離合踏板,沒有什麼配置可言。座椅是典型的沙發座椅,非常厚實,車廂空間巨大,基本就是國內的小巴一樣,就是這麼平常的一輛車,卻受到無數汽車愛好者的追捧,它的設計,就是最主要的原因。

第四代Supra搭載了一款3.0升直列6缸雙渦輪增壓發動機,原廠狀態下能夠提供280馬力,經過小幅度強化后很輕鬆就能夠達到五六百匹馬力,還有很多發燒友能夠讓Supra壓榨出1000匹以上的馬力。

原廠狀態的Supra幾乎是看不到,因為車主們都會讓它解除封印,這樣做才是對它尊重表現,所以在很多汽車素材的電影中,你看到的Supra永遠都是經過爆改的,炫酷的車身顏色,五顏六色的霓虹燈,不要以為這輛車很完美,其實神車也是有缺點的,Supra有着“直路王”的稱號,從這稱號中大概已經能夠看出點什麼了吧,糟糕的操控性能是它最致命的弱點。

Elise S只是搭載了一台來自豐田的4缸1.8L雙VVT-i机械增壓發動機,最大功率僅為220馬力,你肯定會覺得在開玩笑,其實Elise S整車重量只有924kg,其推重比為238馬力/噸,保時捷911 2016款 Carrera 3.0T的推重比也僅僅是243馬力/噸,可想而知Elise S擁有着怎樣的性能。

百公里加速4.6s,異常靈敏的油門踏板,沒有阻力的轉向系統,所有的路面信息都反饋到方向盤上,極致還原了什麼是最原始的駕駛機器,彷彿開過Elise S后才知道什麼是“車”,每個男人心裏都應該隱藏着這樣的一輛車,它不需要什麼豪華質感,也不需要什麼大排量大馬力,更不需要高昂的售價,足夠原汁原味才是最重要的。

雖然說現在的高爾夫GTI和高爾夫R都比第四代Golf R32要更加聰明,科技含量無疑也是不同等級的,但是這些都不影響它成為永遠的最強大眾,它使用3.2升排量的VR6發動機,可以爆發出240馬力,百公里加速6.6秒,最高時速可達247km/h。

最讓人羡慕的不是其強大的性能,而是它那非常低調的外表,除了前後包圍、側裙、雙出排氣管,還有R32的標識,基本上就和普通版的高爾夫沒什麼區別,走在路上你絕不會輕易發現到它。

本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※別再煩惱如何寫文案,掌握八大原則!

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※超省錢租車方案

※教你寫出一流的銷售文案?

網頁設計最專業,超強功能平台可客製化

※產品缺大量曝光嗎?你需要的是一流包裝設計!

台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

“南北奧迪”來了 上汽大眾與奧迪品牌正式牽手

而坊間盛傳,上汽大眾與奧迪合資以後的第一款車型預計將會在2017年4月正式發布。其實上汽才是奧迪最初的國產始祖奧迪品牌與上汽大眾合作並非偶然,早在八十年代,最早的國產奧迪汽車就是由上海大眾公司進行組裝,而當年的“奧迪100”轎車,也是奧迪品牌進入國人視野較早的經典車型。

就在人們都在某寶某東上瘋狂買買買的時候,國內汽車界也出現了一個震動幅度不算小的新聞,11月11日,上汽大眾與奧迪正式簽署合作協議,這意味着大眾旗下豪華品牌奧迪在不久的將來將會在國內有兩家合資企業。

根據相關消息透露,上汽大眾與奧迪合資成立的的銷售公司二者股權各佔50%,雙方合作形式類似於上汽大眾與斯柯達品牌之間的合作形式,即是在未來,上汽大眾以產品代工的形式生產以及銷售奧迪品牌的產品。

上汽大眾與一汽大眾,常常被人稱為“南北大眾”,上汽大眾與奧迪的合作,或許也就意味着將來即將呈現“南北奧迪”爭相奪取豪車市場的局面。而坊間盛傳,上汽大眾與奧迪合資以後的第一款車型預計將會在2017年4月正式發布。

其實上汽才是奧迪最初的國產始祖

奧迪品牌與上汽大眾合作並非偶然,早在八十年代,最早的國產奧迪汽車就是由上海大眾公司進行組裝,而當年的“奧迪100”轎車,也是奧迪品牌進入國人視野較早的經典車型。

在八十年代末,一汽與奧迪簽署了技術轉讓合同,從彼時開始,一汽大眾奧迪迎來了一個長盛不衰的王朝,直至今天,奧迪汽車仍舊是國人心中極具分量的豪華汽車品牌。

奧迪與上汽大眾合作,將會生產出什麼樣的車型是所有車迷最關心的問題,由於大眾集團是將“汽車生產平台模塊化”玩得最溜的汽車廠商,在一汽大眾產品線已經非常成熟的今天進行合作,將來的“上汽大眾奧迪”是以引進奧迪品牌在國內從未生產過的車型進行投產,還是利用現有的產品布局生產同平台但不同命名的車型,我們也將對“上汽大眾奧迪”保持高度關注與跟進。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計最專業,超強功能平台可客製化

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※回頭車貨運收費標準

※推薦評價好的iphone維修中心

※教你寫出一流的銷售文案?

台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!

台中搬家公司費用怎麼算?