前端學HTTP之客戶端識別和cookie


前面的話

  Web服務器可能會同時與數千個不同的客戶端進行對話。這些服務器通常要記錄下它們在與誰交談,而不會認為所有的請求都來自匿名的客戶端。本文主要介紹客戶端識別及cookie機制

 

HTTP首部

  HTTP最初是一個匿名、無狀態的請求/響應協議。服務器處理來自客戶端的請求,然后向客戶端回送一條響應。Web服務器幾乎沒有什么信息可以用來判定是哪個用戶發送的請求,也無法記錄來訪用戶的請求序列

  Web站點希望能夠提供個性化接觸。它們希望對連接另一端的用戶有更多的了解,並且能在用戶瀏覽頁面時對其進行跟蹤。以購物網站為例,專門為用戶生成的歡迎詞和頁面內容,使購物體驗更加個性化;通過了解客戶的興趣,商店可以推薦一些它們認為客戶會感興趣的商品。商店還可以在臨近客戶生日或其他一些重要日子的時候提供生日特定的商品;在線購物的用戶不喜歡一次又一次地填寫繁瑣的地址和信用卡信息。有些站點會將這些管理細節存儲在一個數據庫中。只要他們識別出用戶,就可以使用存檔的管理信息,使得購物體驗更加便捷;HTTP事務是無狀態的。每條請求/響應都是獨立進行的。很多Web站點希望能在用戶與站點交互的過程中(比如,使用在線購物車的時候)構建增量狀態。要實現這一功能,Web站點就需要有一種方式來區分來自不同用戶的HTTP事務

  下表中給出了七種最常見的用來承載用戶相關信息的HTTP請求首部

  From首部包含了用戶的E-mail地址。每個用戶都有不同的E-mail地址,所以在理想情況下,可以將這個地址作為可行的源端來識別用戶。但由於擔心那些不講道德的服務器會搜集這些E-mail地址,用於垃圾郵件的散發,所以很少有瀏覽器會發送From首部。實際上,From首部是由自動化的機器人或蜘蛛發送的,這樣在出現問題時,網管還有個地方可以發送憤怒的投訴郵件

  User-Agent首部可以將用戶所用瀏覽器的相關信息告知服務器,包括程序的名稱和版本,通常還包含操作系統的相關信息。要實現定制內容與特定的瀏覽器及其屬性間的良好互操作時,這個首部是非常有用的,但它並沒有為識別特定的用戶提供太多有意義的幫助

  下面是最新版本chrome發送的:

User-Agent:Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.75 Safari/537.36

  Referer首部提供了用戶來源頁面的URL。Referer首部自身並不能完全標識用戶,但它確實說明了用戶之前訪問過哪個頁面。通過它可以更好地理解用戶的瀏覽行為,以及用戶的興趣所在。比如,如果從一個籃球網站抵達某個Web服務器的,這個服務器可能會推斷你是個籃球迷

  但是,From、User-Agent和Referer首部都不足以實現可靠的識別

 

IP地址

  早期Web曾嘗試將客戶端IP地址作為一種標識形式使用。如果每個用戶都有不同的IP地址,IP地址(如果會發生變化的話)也很少會發生變化,而且Web服務器可以判斷出每條請求的客戶端IP地址的話,這種方案是可行的。通常在HTTP首部並不提供客戶端的IP地址,但Web服務器可以找到承載HTTP請求的TCP連接另一端的IP地址

  比如,在Unix系統中,函數調用getpeername就可以返回發送端機器的客戶端IP地址:

status = getpeername(tcp_connection_socket,...);

  但是,使用客戶端IP地址來識別用戶存在着很多缺點,限制了將其作為用戶識別技術的效能。因為客戶端IP地址描述的是所用的機器,而不是用戶。如果多個用戶共享同一台計算機,就無法對其進行區分了;很多因特網服務提供商都會在用戶登錄時為其動態分配IP地址。用戶每次登錄時,都會得到一個不同的地址,因此Web服務器不能假設IP地址可以在各登錄會話之間標識用戶;為了提高安全性,並對稀缺的地址資源進行管理,很多用戶都是通過網絡地址轉換(Network Address Translation, NAT)防火牆來瀏覽網絡內容的。這些NAT設備隱藏了防火牆后面那些實際客戶端的IP地址,將實際的客戶端IP地址轉換成了一個共享的防火牆IP地址和不同的端口號;HTTP代理和網關通常會打開一些新的、到原始服務器的TCP連接。Web服務器看到的將是代理服務器的IP地址,而不是客戶端的。有些代理為了繞過這個問題會添加特殊的Client-IP或X-Forwarded-For擴展首部來保存原始的IP地址,但並不是所有的代理都支持這種行為

  有些Web站點仍然使用客戶端IP地址在會話之間跟蹤用戶的行為,但這種站點並不多。無法用IP地址確定目標的地方太多了。少數站點甚至將客戶端IP地址作為一種安全特性使用,它們只向來自特定IP地址的用戶提供文檔。在內部網絡中可能可以這么做,但在因特網上就不行了,主要是因為因特網上IP地址太容易偽造了。路徑上如果有攔截代理也會破壞此方案

 

用戶登錄

  Web服務器無需被動地根據用戶的IP地址來猜測他的身份,它可以要求用戶通過用戶名和密碼進行認證登錄來顯式地詢問用戶是誰

  為了使Web站點的登錄更加簡便,HTTP中包含了一種內建機制,可以用www- Authenticate首部和Authorization首部向Web站點傳送用戶的相關信息。一旦登錄,瀏覽器就可以不斷地在每條發往這個站點的請求中發送這個登錄信息了。這樣,就總是有登錄信息可用了

  如果服務器希望在為用戶提供對站點的訪問之前,先行登錄,可以向瀏覽器回送一條HTTP響應代碼401 Login Required。然后,瀏覽器會顯示一個登錄對話框,並用Authorization首部在下一條對服務器的請求中提供這些信息

  在圖a中,瀏覽器對站點www.joes-hardware.com發起了一條請求;站點並不知道這個用戶的身份,因此在圖b中,服務器會返回401 Login Required HTTP響應碼,並添加www-Authentication首部,要求用戶登錄。這樣瀏覽器就會彈出一個登錄對話框;只要用戶輸入了用戶名和密碼(對其身份進行完整性檢査),瀏覽器就會重復原來的請求。這次它會添加一個Authorization首部,說明用戶名和密碼。對用戶名和密碼進行加密,防止那些有意無意的網絡觀察者看到;現在,服務器已經知道用戶的身份了,今后的請求要使用用戶名和密碼時,瀏覽器會自動將存儲下來的值發送出去,甚至在站點沒有要求發送的時候也經常會向其發送。瀏覽器在每次請求中都向服務器發送Authorization首部作為一種身份的標識,這樣,只要登錄一次,就可以在整個會話期間維持用戶的身份了

  但是,登錄多個Web站點是很繁瑣的。從一個站點瀏覽到另一個站點的時候,需要在每個站點上登錄。更糟的是,很可能要為不同的站點記住不同的用戶名和密碼。訪問很多站點,喜歡的用戶名可能已經被其他人用過了,而且有些站點為用戶名和密碼的長度和組成設置了不同的規則

 

胖URL

  有些Web站點會為每個用戶生成特定版本的URL來追蹤用戶的身份。通常,會對真正的URL進行擴展,在URL路徑開始或結束的地方添加一些狀態信息。用戶瀏覽站點時,Web服務器會動態生成一些超鏈,繼續維護URL中的狀態信息

  改動后包含了用戶狀態信息的URL被稱為胖URL(fat URL)。下面是Amazon.com使用的一些胖URL實例。每個URL后面都附加了一個用戶特有的標識碼,在這個例子中就是002-1145265-8016838,這個標識碼有助於在用戶瀏覽商店內容時對其進行跟蹤

<a href="/exec/obidos/tg/browse/-/229220/ref=gr_gifts/002-1145265-8016838">All Gifts</a><br>
<a href="/exec/obidos/wishlist/ref=gr_pll_/002-1145265-8016838">Wish List</a><br>

  可以通過胖URL將Web服務器上若干個獨立的HTTP事務捆綁成一個“會話”或“訪問”。用戶首次訪問這個Web站點時,會生成一個唯一的ID,用服務器可以識別的方式將這個ID添加到URL中去,然后服務器就會將客戶端重新導向這個胖URL。不論什么時候,只要服務器收到了對胖URL的請求,就可以去査找與那個用戶ID相關的所有增量狀態(購物車、簡介等),然后重寫所有的輸出超鏈,使其成為胖URL,以維護用戶的ID

  可以在用戶瀏覽站點時,用胖URL對其進行識別。但這種技術存在幾個很嚴重的問題:丑陋的URL——瀏覽器中顯示的胖URL會給新用戶帶來困擾;無法共享URL——胖URL中包含了與特定用戶和會話有關的狀態信息。如果將這個URL發送給其他人,可能就在無意中將個人信息都共享出去了;破壞緩存——為每個URL生成用戶特有的版本就意味着不再有可供公共訪問的URL需要緩存了;額外的服務器負荷——服務器需要重寫HTML頁面使URL變胖;逃逸口——用戶跳轉到其他站點或者請求一個特定的URL時,就很容易在無意中“逃離”胖URL會話,只有當用戶嚴格地追隨預先修改過的鏈接時,胖URL才能工作。如果用戶逃離此鏈接,就會丟失他的進展(可能是一個已經裝滿了東西的購物車)信息,得重新開始;在會話間是非持久的,除非用戶收藏了特定的胖URL,否則用戶退出登錄時,所有的信息都會丟失

 

cookie

  cookie是當前識別用戶,實現持久會話的最好方式。前面各種技術中存在的很多問題對它們都沒什么影響,但是通常會將它們與那些技術共用,以實現額外的價值

  cookie最初是由網景公司開發的,但現在所有主要的瀏覽器都支持它。cookie非常重要,而且它們定義了一些新的HTTP首部。cookie的存在也影響了緩存,大多數緩存和瀏覽器都不允許對任何cookie的內容進行緩存

【類型】

  可以籠統地將cookie分為兩類:會話cookie和持久cookie。會話cookie是一種臨時cookie,它記錄了用戶訪問站點時的設置和偏好。用戶退出瀏覽器時,會話cookie就被刪除了。持久cookie的生存時間更長一些,它們存儲在硬盤上。瀏覽器退出,計算機重啟時它們仍然存在。通常會用持久cookie維護某個用戶會周期性訪問的站點的配置文件或登錄名

  會話cookie和持久cookie之間唯一的區別就是它們的過期時間。如果設置了Discard參數,或者沒有設置Expires或Max-Age參數來說明擴展的過期時間,這個cookie就是一個會話cookie

【工作機制】

  用戶首次訪問Web站點時,Web服務器對用戶一無所知。Web服務器希望這個用戶會再次回來,所以想給這個用戶“拍上”一個獨有的cookie,這樣以后它就可以識別出這個用戶了。cookie中包含了一個由名字=值(namezvalue)這樣的信息構成的任意列表,並通過Set-Cookie或Set-Cookie2 HTTP響應(擴展)首部將其貼到用戶身上去

  cookie中可以包含任意信息,但它們通常都只包含一個服務器為了進行跟蹤而產生的獨特的識別碼。比如,服務器會將一個表示id=”34294”的cookie貼到用戶上去。服務器可以用這個數字來查找服務器為其訪問者積累的數據庫信息(購物歷史、地址信息等)

  但是,cookie並不僅限於ID號。很多Web服務器都會將信息直接保存在cookie中

Cookie: name "Brian Totty"; phone="555-1212"

  瀏覽器會記住從服務器返回的Set-Cookie或Set-Cookie2首部中的cookie內容,並將cookie集存儲在瀏覽器的cookie數據庫中。將來用戶返回同一站點時,瀏覽器會挑中那個服務器貼到用戶上的那些cookie,並在一個cookie請求首部中將其傳回去

【cookie罐:客戶端的狀態】

  cookie的基本思想就是讓瀏覽器積累一組服務器特有的信息,每次訪問服務器時都將這些信息提供給它。因為瀏覽器要負責存儲cookie信息,所以此系統被稱為客戶端側狀態(client-side state)。這個cookie規范的正式名稱為HTTP狀態管理機制(HTTP state management mechanism)

  瀏覽器內部的cookie罐中可以有成百上千個cookie,但瀏覽器不會將每個cookie都發送給所有的站點。實際上,它們通常只向每個站點發送2-3個cookie。原因如下:對所有這些cookie字節進行傳輸會嚴重降低性能。瀏覽器實際傳輸的cookie字節數要比實際的內容字節數多;cookie中包含的是服務器特有的名值對,所以對大部分站點來說,大多數cookie都只是無法識別的無用數據;將所有的cookie發送給所有站點會引發潛在的隱私問題,那些不信任的站點也會獲得只想發給其他站點的信息

  總之,瀏覽器只向服務器發送服務器產生的那些cookie。joes-hardware.com產生的cookie會被發送給joes-hardware.com,不會發送給bobs-books.com或marys-movies.com

  很多Web站點都會與第三方廠商達成協議,由其來管理廣告。這些廣告被做得像Web站點的一個組成部分,而且它們確實發送了持久cookie。用戶訪問另一個由同一廣告公司提供服務的站點時,由於域是匹配的,瀏覽器就會再次回送早先設置的持久cookie。營銷公司可以將此技術與Referer首部結合,暗地里構建一個用戶檔案和瀏覽習慣的詳盡數據集。現代的瀏覽器都允許用戶對隱私特性進行設置,以限制第三方cookie的使用

  1、cookie的域屬性

  產生cookie的服務器可以向Set-Cookie響應首部添加一個Domain屬性來控制哪些站點可以看到那個cookie。比如,下面的HTTP響應首部就是在告訴瀏覽器將cookie user= "maryl7"發送給域".airtravelbargains.com"的所有站點

Set-cookie: user="maryl7"; domain="airtravelbargains.com"

  如果用戶訪問的是www.airtravelbargains.com、specials.airtravelbargains.com或任意以.airtravelbargains.com結尾的站點,下列Cookie首部都會被發布出去:

Cookie: user="maryl7"

  2、cookie路徑屬性

  cookie規范甚至允許用戶將cookie與部分Web站點關聯起來。可以通過Path屬性來實現這一功能,在這個屬性列出的URL路徑前綴下所有cookie都是有效的

  例如,某個Web服務器可能是由兩個組織共享的,每個姐織都有獨立的cookie。站點www.airtravelbargains.com可能會將部分Web站點用於汽車租賃——比如,http://www.airtravelbargains.com/autos/——用一個獨立的cookie來記錄用戶喜歡的汽車尺寸。可能會生成一個如下所示的特殊汽車租賃cookie:

Set-cookie: pref=compact; domain="airtravelbargains.com"; path=/autos/

  如果用戶訪問http://www.airtravelbargains.com/specials.html,就只會獲得這個cookie:

Cookie: user="maryl7"

  但如果訪問http://www.airtravelbargains.com/autos/cheapo/index.html,就會獲得這兩個cookie:

Cookie: user="maryl7"
Cookie: pref=compact

  因此,cookie就是由服務器貼到客戶端上,由客戶端維護的狀態片段,只會回送給那些合適的站點。下面我們來更仔細地看看cookie的技術和標准

 【cookie成分】

  現在使用的cookie規范有兩個不同的版本:cookies版本0(有時被稱為Netscape cookies)和cookies版本1(RFC 2965)。cookies版本1 是對cookies版本0的擴展,應用不如后者廣泛

  1、Cookies版本0

  最初的cookie規范是由網景公司定義的,這些“版本0”的cookie定義了set-Cookie響應首部、cookie請求首部以及用於控制cookie的字段

Set-Cookie: name-value[;expires=date][;path=path][;domain=domain][;secure]
Cookie: name1-value1[; name2=value2]...

  Set-Cookie首部有一個強制性的cookie名和cookie值。后面跟着可選的cookie屬性,中間由分號分隔

 

  客戶端發送請求時,會將所有與域、路徑和安全過濾器相匹配的未過期cookie都發送給這個站點。所有cookie都被組合到一個Cookie首部中

Cookie: session-id=002-1145265-8016838; session-id-time=1007884800

  2、Cookies版本1

  RFC 2965(以前的RFC 2109)定義了一個cookie的擴展版本。這個版本1標准引入了Set-cookie2首部和Cookie2首部,但它也能與版本0系統進行互操作

  RFC 2965 cookie標准比原始的網景公司的標准略微復雜一些,還未得到完全的支持。RFC 2965 cookie的主要改動包括下列內容:為每個cookie關聯上解釋性文本,對其目的進行解釋;允許在瀏覽器退出時,不考慮過期時間,將cookie強制銷毀;用相對秒數,而不是絕對日期來表示cookie的Max-Age;通過URL端口號,而不僅僅是域和路徑來控制cookie的能力;通過Cookie首部回送域、端口和路徑過濾器(如果有的話);為實現互操作性使用的版本號;在Cookie首部從名字中區分出附加關鍵字的$前綴

  cookie版本1的語法如下所示:

set-cookie             =   "Set-Cookie2:" cookies
cookies                =   1#cookie
cookie                 =   NAME "=" VALUE *(";" set-cookie-av)
NAME                   =   attr
VALUE                  =   value
set-cookie-av          =   "Comment" "=" value
                           "CommentURL" "=" <"> hctp_URL <"> "Discard"
                           "Domain" "=" value
                           "Max-Age" "=" value
                           "Path" "=" value
                           "Port" [ "=" <"> portlist <"> ]
                           "Secure"
                           "Version" "=" 1*DIGIT
portlist               =   1#portnum
portnum                =   1*DIGIT
cookie                 =   "Cookie:" cookie-version 1*((";" | ",") cookie-value)
cookie-value           =   NAME "=" VALUE [";" path][";" domain][";" port]
cookie-version         =   "$Version" "=" value
NAME                   =   attr
VALUE                  =   value
path                   =   "$Path" "=" value
domain                 =   "$Domain" "=" value
port                   =   "$Port" [ "=" <"> value <"> ]
cookie2                =   "Cookie2:" cookie-version

  版本1的cookie標准比網景公司標准的可用屬性要多。下表對這些屬性做了快速匯總。更詳細的解釋請參見RFC2965

  版本1的cookie會帶回與傳送的每個cookie相關的附加信息,用來描述每個cookie途徑的過濾器。每個匹配的cookie都必須包含來自相應Set-Cookie2首部的所有Domain、Port或Path屬性

  比如,假設客戶端以前曾收到下列五個來自Web站點www.joes-hardware.com的Set-Cookie2響應

Set-Cookie2: ID="29046"; Domain=".joes-hardware.com"
Set-Cookie2: color=blue
Set-Cookie2: support-pref="L2";Domain="customer-care.joes-hardware.com"
Set-Cookie2: Coupon="hammer027"; Version="1"; Path="/tools"
Set-Cookie2: Coupon="handvac103"; Version="l”; Path="/tools/cordless"

  如果客戶端對路徑/tools/cordless/specials.html又發起了一次請求,會同時發送這樣一個很長的Cookie首部

Cookie:    $Version="l";
          ID-"29046";$Domain=".joes-hardware.com";
          color="blue";
          Coupon="hammer027"; $Path="/tools";
          Coupon="handvac103"; $Path="/tools/cordless"

  所有匹配cookie都是和它們的set-Cookie2過濾器一同傳輸的,而且保留關鍵字都是以美元符號($)開頭的

  Cookie2請求首部負責在能夠理解不同cookie規范版本的客戶端和服務器之間進行互操作性的協商。Cookie2首部告知服務器,用戶Agent代理理解新形式的cookie,並提供了所支持的cookie標准版本(將其稱為Cookie-Version更合適一些):

Cookie2:    $Version="1"

  如果服務器理解新形式的cookie,就能夠識別出Cookie2首部,並在響應首部發送Set-Cookie2(而不是Set-Cookie)。如果客戶端從同一個響應中既獲得了Set-Cookie首部,又獲得了Set-Cookie2首部,就會忽略老的Set-Cookie首部

  如果客戶端既支持版本0又支持版本1的cookie,但從服務器獲得的是版本0的Set-Cookie首部,就應該帶着版本0的Cookie首部發送cookie。但客戶端還應該發送Cookie2: $Version="1"來告知服務器它是可以升級的

【cookie與會話跟蹤】

  可以用cookie在用戶與某個Web站點進行多項事務處理時對用戶進行跟蹤。電子商務Web站點用會話cookie在用戶瀏覽時記錄下用戶的購物車信息。以流行的購物網站Amazon.com為例。在瀏覽器中輸入http://www.amazon.com時,就啟動了一個事務鏈,在這些事務中Web服務器會通過一系列的重定向、URL重寫以及cookie設置來附加標識信息

  下圖顯示了從一次Amazon.com訪問中捕獲的事務序列

  圖a——瀏覽器首次請求Amazon.com根頁面

  圖b——服務器將客戶端重定向到一個電子商務軟件的URL上

  圖c——客戶端對重定向的URL發起一個請求

  圖d——服務器在響應上貼上兩個會話cookie,並將用戶重定向到另一個URL,這樣客戶端就會用這些附加的cookie再次發出請求。這個新的URL是個胖URL,也就是說有些狀態嵌入到URL中去了。如果客戶端禁止了cookie,只要用戶一直跟隨着Amazon.com產生的胖URL鏈接,不離開網站,仍然可以實現一些基本的標識功能

  圖e——客戶端請求新的URL,但現在會傳送兩個附加的cookie

  圖f——服務器重定向到home.html頁面,並附加另外兩個cookie

  圖g——客戶端獲取home.html頁面並將所有四個cookie都發送出去

  圖h——服務器回送內容

【cookie與緩存】

  緩存那些與cookie事務有關的文檔時要特別小心。因為不希望給用戶分配一個過去某些用戶用過的cookie,或者更糟糕的是,向一個用戶展示其他人私有文檔的內容

  cookie和緩存的規則並沒有很好地建立起來。下面是處理緩存時的一些指導性規則

  如果無法緩存文檔,要將其標示出來。文檔的所有者最清楚文檔是否是不可緩存的。如果文檔不可緩存,就顯式地注明——具體來說,如果除了Set-Cookie首部之外文檔是可緩存的,就使用Cache-Control:no-cache="Sec-Cookie"。另一種更通用的做法是為可緩存文檔使用Cache-Control:public,這樣有助於節省Web中的帶寬

  緩存Set-Cookie首部時要小心。如果響應中有Set-Cookie首部,就可以對主體進行緩存,除非被告知不要這么做。但要注意對Set-Cookie首部的緩存。如果向多個用戶發送了相同的Set-Cookie首部,可能會破壞用戶的定位

  有些緩存在將響應緩存起來之前會刪除Set-Cookie首部,但這樣也會引發一
些問題,因為在沒有緩存的時候,通常都會有cookie貼在客戶端上,但由緩存提供服務的客戶端就不會有cookie了。強制緩存與原始服務器重新驗證每條請求,並將返回的所有Set-Cookie首部都合並到客戶端的響應中去,就可以改善這種狀況。原始服務器可以通過向緩存的副本中添加這個首部來要求進行這種再驗證

Cache-Control: must-revalidate, max-age=0

  即便內容實際上是可以緩存的,比較保守的緩存可能也會拒絕緩存所有包含Set-Cookie首部的響應。有些緩存允許使用緩存Set-Cookie圖片,但不緩存文本的模式

  小心處理帶有Cookie首部的請求。帶有Cookie首部的請求到達時,就在提示我們,得到的結果可能是私有的。一定要將私有內容標識為不可緩存的,但有些服務器可能會犯錯,沒有將此內容標記為不可緩存的

  有些響應文檔對應於攜帶Cookie首部的請求,保守的緩存可能會選擇不去緩存這些響應文檔。同樣,有些緩存允許使用緩存cookie圖片,而不緩存文本的模式。得到更廣泛接受的策略是緩存帶有Cookie首部的圖片,將過期時間設置為零,強制每次都進行再驗證

【安全性和隱私】

  cookie是可以禁止的,而且可以通過日志分析或其他方式來實現大部分跟蹤記錄,所以cookie自身並不是很大的安全隱患。實際上,可以通過提供一個標准的審査方法在遠程數據庫中保存個人信息,並將匿名cookie作為鍵值,來降低客戶端到服務器的敏感數據傳送頻率

  但是,潛在的濫用情況總是存在的。所以,在處理隱私和用戶跟蹤信息時,最好還是要小心一些。第三方Web站點使用持久cookie來跟蹤用戶就是一種最大的濫用。將這種做法與IP地址和Referer首部信息結合在一起,這些營銷公司就可以構建起相當精確的用戶檔案和瀏覽模式信息

  盡管有這么多負面的宣傳,人們通常還是認為,如果能夠小心地確認在向誰提供私人信息,並仔細査閱站點的隱私政策。那么,cookie會話處理和事務處理所帶來的便利性要比大部分風險更重要


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM