電子商務(wù)安全編碼對(duì)于從事電子商務(wù)技術(shù)開(kāi)發(fā)的人員至關(guān)重要, 是電子商務(wù)安全理論的具體實(shí)踐。本書(shū)劃分為三大部分: PHP安全、Java EE安全、ASP.NET安全。在每一部分中, 按照信息暴露、安全輸入過(guò)濾、安全輸出、安全傳輸、安全存儲(chǔ)幾個(gè)環(huán)節(jié)進(jìn)行了勾勒。精心選擇實(shí)例素材, 尤其對(duì)于電子商務(wù)中敏感模塊的編碼實(shí)現(xiàn), 作了非常詳細(xì)的分析。
本書(shū)并不講述電子商務(wù)網(wǎng)站的宏觀體系開(kāi)發(fā),如用戶(hù)的需求、分析、設(shè)計(jì)、實(shí)現(xiàn)等。本書(shū)著眼于戰(zhàn)術(shù)運(yùn)用,重點(diǎn)講述電子商務(wù)編碼的安全實(shí)現(xiàn)環(huán)節(jié)。本書(shū)強(qiáng)調(diào)的安全編碼技術(shù)也不是編程的全部技巧,而是程序員在編碼當(dāng)中*容易忽視的環(huán)節(jié),還有電子商務(wù)網(wǎng)站獨(dú)特的安全需求在技術(shù)上的解決。盡管就軟件本身來(lái)說(shuō),可重用性、開(kāi)放性、封閉性都能從一定程度上保證編碼本身的生命力更持久,它們當(dāng)然提高了編碼的安全度。但本書(shū)并不論述這些編程宏觀方法論的東西,本書(shū)的目的是讓讀者理解當(dāng)前三大編程體系的安全基礎(chǔ)和應(yīng)用技術(shù)。因此,本書(shū)也不論述加密算法的編程實(shí)現(xiàn)問(wèn)題,也不討論各種安全漏洞,而是研究對(duì)程序員來(lái)說(shuō)編程應(yīng)如何安全實(shí)現(xiàn)。
安全的電子商務(wù)Web應(yīng)用開(kāi)發(fā)首先要從對(duì)HTTP協(xié)議的深入理解開(kāi)始,因?yàn)镠TTP協(xié)議是在客戶(hù)端和服務(wù)器端之間運(yùn)輸HTML網(wǎng)頁(yè)的車(chē)輛,這輛車(chē)有兩種車(chē)頭:request頭和response頭?蛻(hù)端必須獲得一個(gè)有安全意識(shí)的程序員注意,它是典型的Web瀏覽器、蜘蛛機(jī)器人或者其他具有風(fēng)險(xiǎn)的軟件(比如自動(dòng)注冊(cè)提交機(jī))。HTTP協(xié)議通過(guò)TCP的三次握手機(jī)制連接到Web服務(wù)器。一旦服務(wù)器收到請(qǐng)求消息,服務(wù)器就開(kāi)始處理請(qǐng)求和發(fā)回帶有狀態(tài)行的響應(yīng)(如HTTP/1.1 200 OK)。然后,服務(wù)器把剩下的響應(yīng)消息發(fā)送出去,其中可能包含HTML、圖像、音頻、錯(cuò)誤消息或任何其他想要發(fā)送的信息。瀏覽器發(fā)出的HTTP請(qǐng)求類(lèi)型有GET、POST,還有用在編程中的HEAD、PUT和TRACE類(lèi)型。
電子商務(wù)程序員編寫(xiě)出一個(gè)存在漏洞的Web頁(yè)面,表面上看起來(lái)可能不是什么大問(wèn)題,但卻可以造成非常嚴(yán)重的問(wèn)題,包括以下內(nèi)容。
(1) 盜竊賬戶(hù)或服務(wù)。當(dāng)Web站點(diǎn)使用會(huì)話(huà)狀態(tài)時(shí),會(huì)話(huà)標(biāo)識(shí)符通常存儲(chǔ)在用戶(hù)瀏覽器的cookie 中,可以通過(guò)JavaScript查看和操作站點(diǎn)的cookie。攻擊者可以利用此功能把cookie的內(nèi)容定向到攻擊者擁有的另一個(gè)站點(diǎn)上,也可以在自己主機(jī)的瀏覽器上重新創(chuàng)建這個(gè)cookie,攻擊者看上去好像是Web服務(wù)的原始用戶(hù)。根據(jù)被攻擊站點(diǎn)的不同,對(duì)于那些已經(jīng)被盜取信息的用戶(hù),可能會(huì)導(dǎo)致身份盜竊、訪問(wèn)機(jī)密信息、訪問(wèn)付費(fèi)內(nèi)容或DoS攻擊等不同的后果。
(2) 用戶(hù)重定向。一旦攻擊者發(fā)現(xiàn)了一個(gè)XSS漏洞,他就完全可以使用JavaScript注入重定向整個(gè)瀏覽器。這可能導(dǎo)致安裝間諜軟件、網(wǎng)絡(luò)釣魚(yú)或其他常見(jiàn)性的危害。
(3) 用戶(hù)跟蹤。因?yàn)镴avaScript可以操作頁(yè)面內(nèi)容,所以攻擊者可以在服務(wù)器托管的存在漏洞的頁(yè)面中插入圖像,這個(gè)圖像可以被用來(lái)跟蹤多個(gè)存在漏洞的Web站點(diǎn)的用戶(hù)。此外,攻擊者可以利用JavaScript通過(guò)添加單擊事件腳本來(lái)替換頁(yè)面上所有的鏈接,以便收集更多的統(tǒng)計(jì)信息。
(4) 錯(cuò)誤信息。攻擊者可以使用JavaScript重寫(xiě)Web頁(yè)面內(nèi)容。如果被攻擊的站點(diǎn)是一個(gè)金融Web站點(diǎn),攻擊者為了修改特定的股票價(jià)格來(lái)修改站點(diǎn)中的頁(yè)面,那么用戶(hù)沒(méi)有任何辦法辨別顯示的價(jià)格是不正確的價(jià)格,因?yàn)闉g覽器中的URL是相同的。
(5) 瀏覽器插件的安裝和利用。攻擊者可以把標(biāo)記插入頁(yè)面中,標(biāo)記可以啟動(dòng)ActiveX控件、Flash、Java或其他的以這種方式控制的插件,然后利用該插件的漏洞來(lái)竊取或公開(kāi)用戶(hù)的信息。
(6) DoS攻擊。攻擊者在一個(gè)想要發(fā)動(dòng)DoS攻擊的站點(diǎn)上插入一個(gè)圖像標(biāo)記,并加載*大的圖像。由于此站點(diǎn)有足夠大的用戶(hù)瀏覽量,只加載這些圖像就需要占用很大的帶寬,因此該站點(diǎn)就無(wú)法再很好地提供其他Internet服務(wù)了。而且,加載圖像屬于正常行為,所以此攻擊很難被發(fā)現(xiàn)。
有鑒于此,開(kāi)發(fā)者必須掌握安全的Web應(yīng)用編程技能,才能夠滿(mǎn)足電子商務(wù)的需求。
本書(shū)并不講述電子商務(wù)網(wǎng)站的宏觀體系開(kāi)發(fā),如用戶(hù)的需求、分析、設(shè)計(jì)、實(shí)現(xiàn)等。本書(shū)著眼于戰(zhàn)術(shù)運(yùn)用,重點(diǎn)講述電子商務(wù)編碼的安全實(shí)現(xiàn)環(huán)節(jié)。本書(shū)強(qiáng)調(diào)的安全編碼技術(shù)也不是編程的全部技巧,而是程序員在編碼當(dāng)中*容易忽視的環(huán)節(jié),還有電子商務(wù)網(wǎng)站獨(dú)特的安全需求在技術(shù)上的解決。盡管就軟件本身來(lái)說(shuō),可重用性、開(kāi)放性、封閉性都能從一定程度上保證編碼本身的生命力更持久,它們當(dāng)然提高了編碼的安全度。但本書(shū)并不論述這些編程宏觀方法論的東西,本書(shū)的目的是讓讀者理解當(dāng)前三大編程體系的安全基礎(chǔ)和應(yīng)用技術(shù)。因此,本書(shū)也不論述加密算法的編程實(shí)現(xiàn)問(wèn)題,也不討論各種安全漏洞,而是研究對(duì)程序員來(lái)說(shuō)編程應(yīng)如何安全實(shí)現(xiàn)。
安全是一個(gè)程度性的詞匯,沒(méi)有絕對(duì)的安全,只有相對(duì)的安全。安全編碼就是在實(shí)現(xiàn)基本功能的基礎(chǔ)上,增加防護(hù)性編碼,以保證基本功能編碼安全順利地實(shí)現(xiàn)功能。安全編碼和風(fēng)險(xiǎn)編碼(黑客程序)是一對(duì)矛盾體,由安全編碼所建立的軟件對(duì)抗的是有風(fēng)險(xiǎn)的環(huán)境中產(chǎn)生的風(fēng)險(xiǎn)編碼。安全性不是一項(xiàng)能夠事后添加到現(xiàn)有應(yīng)用程序的功能,不能等到開(kāi)發(fā)階段的后期才引入它。安全性是應(yīng)用程序的固有特性,應(yīng)作為設(shè)計(jì)階段的首要任務(wù)來(lái)規(guī)劃。為了確保應(yīng)用程序的安全,需要開(kāi)發(fā)者、架構(gòu)師和管理員齊心協(xié)力。在電子商務(wù)應(yīng)用系統(tǒng)所處的環(huán)境中,惡意用戶(hù)輸入的非正常數(shù)據(jù)是風(fēng)險(xiǎn)的主要原因。
本書(shū)的讀者定位是掌握編程基礎(chǔ)知識(shí)的程序員和學(xué)習(xí)了安全基礎(chǔ)知識(shí)后待實(shí)踐的電子商務(wù)專(zhuān)業(yè)學(xué)生。因此,本書(shū)不再講述安全基礎(chǔ)知識(shí)、術(shù)語(yǔ),回避了三大編程體系的語(yǔ)法講解,回避了服務(wù)器和數(shù)據(jù)庫(kù)管理系統(tǒng)的漏洞討論,也回避了各種安全算法的實(shí)現(xiàn)原理或者它們自身漏洞的討論。本書(shū)側(cè)重于程序員運(yùn)用編程工具提供的安全API的運(yùn)用,強(qiáng)調(diào)面向電子商務(wù)應(yīng)用,強(qiáng)調(diào)網(wǎng)頁(yè)安全編寫(xiě)技巧、準(zhǔn)則,強(qiáng)調(diào)編寫(xiě)安全的代碼,強(qiáng)調(diào)網(wǎng)站整體性安全方案和單頁(yè)面安全技巧。也就是說(shuō),本書(shū)僅限于講解如何開(kāi)發(fā)安全的電子商務(wù)應(yīng)用程序。通過(guò)本書(shū)的學(xué)習(xí),讀者能夠在編碼過(guò)程中自覺(jué)運(yùn)用這些技巧和知識(shí),提高編碼的健壯性,從而進(jìn)一步滿(mǎn)足電子商務(wù)類(lèi)軟件的產(chǎn)出質(zhì)量。
一個(gè)程序員編寫(xiě)的程序如果僅僅考慮了基本功能的實(shí)現(xiàn),那么他的程序就如同一個(gè)裸體的嬰兒,沒(méi)有任何抵御外部攻擊的能力。安全編碼就是在實(shí)現(xiàn)了基本功能代碼之外,為他穿衣戴帽、披之以鎧甲,以實(shí)現(xiàn)防護(hù)。在什么地方加鎧甲,加什么樣的鎧甲,在什么情形下加,這些是安全編碼要研究的地方。從安全實(shí)現(xiàn)上來(lái)看,安全編碼主要分為聲明性安全和程序性安全:聲明性安全主要針對(duì)宏觀配置,有利于掌控應(yīng)用全局的安全;程序性安全代碼邏輯上都等價(jià)于if(已識(shí)別風(fēng)險(xiǎn))...then(防護(hù))...else(意外處理)語(yǔ)句,有利于程序細(xì)節(jié)局部的安全防護(hù)。寫(xiě)在前面的代碼是后面代碼執(zhí)行的前提,前提自然會(huì)起到保護(hù)后續(xù)代碼(也就是結(jié)果)的作用。安全外殼保護(hù)的永遠(yuǎn)是脆弱的代碼,脆弱的代碼會(huì)產(chǎn)生脆弱敏感性信息的泄露,歸根到底,防護(hù)*終要保護(hù)的就是敏感數(shù)據(jù)。開(kāi)發(fā)者在開(kāi)發(fā)網(wǎng)頁(yè)程序時(shí),頭腦中應(yīng)該首先去想:①這個(gè)網(wǎng)頁(yè)的安全度是什么級(jí)別;②是否需要驗(yàn)證用戶(hù)身份、訪問(wèn)控制權(quán)限;③這個(gè)網(wǎng)頁(yè)內(nèi)有沒(méi)有需要用戶(hù)大量提交的數(shù)據(jù),如果有就要在設(shè)計(jì)網(wǎng)頁(yè)內(nèi)增加驗(yàn)證碼,以防止自動(dòng)提交式的癱瘓網(wǎng)站攻擊;④程序員要保證這個(gè)頁(yè)面和其他網(wǎng)頁(yè)間的邏輯關(guān)系,限制跨頁(yè)面訪問(wèn)和盜鏈訪問(wèn),在頁(yè)面設(shè)置監(jiān)視量,等等。
本書(shū)分為三大篇。*篇講述了PHP的安全編程知識(shí),PHP面向電子商務(wù)的安全開(kāi)發(fā)。第二篇講述了Java的安全編程知識(shí),JSP面向電子商務(wù)網(wǎng)站的安全編碼問(wèn)題。第三篇講述了ASP.NET的安全編程知識(shí),ASP.NET面向電子商務(wù)網(wǎng)站的安全編碼問(wèn)題。曹杰博士為本書(shū)撰寫(xiě)了整體框架,撰寫(xiě)了相應(yīng)的章節(jié);張艷萍博士為本書(shū)的資料收集、匯總給予了很大支持,并撰寫(xiě)了相應(yīng)的章節(jié)。
編寫(xiě)本書(shū)的目的是給程序員提供一個(gè)安全編程指南。由于本書(shū)編者水平有限,書(shū)中難免存在錯(cuò)誤、瑕疵和其他不足之處,懇請(qǐng)讀者批評(píng)、指正。
編 者
第1篇 PHP安全基礎(chǔ) 1
1.1 信息暴露 1
1.1.1 register_globals和錯(cuò)誤報(bào)告 1
1.1.2 數(shù)據(jù)庫(kù)訪問(wèn)權(quán)限暴露 3
1.1.3 配置選項(xiàng) 4
1.1.4 引入包含帶來(lái)的暴露 6
1.2 輸入過(guò)濾 7
1.2.1 過(guò)濾基礎(chǔ) 7
1.2.2 過(guò)濾表單和URL 10
1.2.3 SQL注入 21
1.2.4 動(dòng)態(tài)包含的未過(guò)濾問(wèn)題 23
1.2.5 文件與命令的未過(guò)濾問(wèn)題 26
1.2.6 驗(yàn)證過(guò)濾與授權(quán) 31
1.2.7 需要關(guān)注輸入過(guò)濾的函數(shù) 38
1.3 輸出轉(zhuǎn)義 40
1.4 安全傳輸 41
1.5 安全存儲(chǔ) 50
1.5.1 共享主機(jī) 50
1.5.2 加密 60
1.5.3 上傳文件的安全存儲(chǔ) 66
1.6 安全開(kāi)發(fā)原則 67
1.7 電子商務(wù)PHP開(kāi)發(fā)實(shí)例 71
1.7.1 安全登錄 71
1.7.2 訂單簽名 85
第2篇 Java安全編碼 88
2.1 Java編程陷阱 90
2.1.1 Java基礎(chǔ)編程陷阱 90
2.1.2 Java客戶(hù)端方面陷阱 95
2.1.3 Java服務(wù)器端方面陷阱 103
2.2 商務(wù)軟件基礎(chǔ)開(kāi)發(fā)語(yǔ)言Java的安全 107
2.2.1 機(jī)密性的實(shí)現(xiàn) 107
2.2.2 完整性的實(shí)現(xiàn) 108
2.2.3 認(rèn)證性的實(shí)現(xiàn) 109
2.2.4 電子商務(wù)安全協(xié)議SSL/TLS/HTTPS的實(shí)現(xiàn) 110
2.3 JSP安全 112
2.4 電子商務(wù)框架安全編碼 113
2.5 支付模塊的安全編碼 121
第3篇 ASP.NET安全 134
3.1 ASP.NET驗(yàn)證和授權(quán)機(jī)制 135
3.2 安全配置 140
3.3 輸入過(guò)濾 143
3.4 輸出轉(zhuǎn)義 149
3.5 安全存儲(chǔ) 150
3.6 ASP.NET電子商務(wù)安全編碼 155
3.6.1 管理訂單 155
3.6.2 搜索商品 161
參考文獻(xiàn) 167
第2篇 Java安全編碼
J2EE體系是企業(yè)級(jí)電子商務(wù)應(yīng)用開(kāi)發(fā)的首選,這是由于J2EE體系做開(kāi)發(fā)非常靈活,能夠給程序員以非常大的發(fā)揮空間。但是J2EE體系同時(shí)要求程序員掌握大量的知識(shí),這增加了編碼出現(xiàn)問(wèn)題的概率。在行業(yè)領(lǐng)域,程序性安全正在向聲明性安全轉(zhuǎn)變,目標(biāo)是通過(guò)策略而不是依靠在每個(gè)應(yīng)用程序中散布安全代碼來(lái)控制保障。
J2EE安全為了實(shí)現(xiàn)可移植性和可重用性,其設(shè)計(jì)主要采用了聲明性方法。在J2EE平臺(tái)上絕大多數(shù)的認(rèn)證、授權(quán)、整合、機(jī)密性和訪問(wèn)控制策略等行為,都是可以使用配置文件和部署描述符來(lái)完成的。而由于這些配置文件和部署描述符是置于應(yīng)用程序之外的,這就減輕了程序員的負(fù)擔(dān),并且使得Java企業(yè)級(jí)程序具有可移植性、可重用性和靈活性。在J2EE環(huán)境中,容器支撐著組件,容器的安全基礎(chǔ)是安全角色。一個(gè)容器可以提供兩種安全:聲明性的和程序性的。
在聲明性安全模型中,一個(gè)應(yīng)用通過(guò)應(yīng)用外部的表單中的安全約束來(lái)表達(dá)它的安全策略,在J2EE中,安全約束是在部署描述符中指定的。這允許應(yīng)用成為所謂的安全機(jī)制不可知或者安全無(wú)意識(shí)。不要求應(yīng)用代碼保證安全或者執(zhí)行應(yīng)用安全策略。使用聲明性安全,應(yīng)用的邏輯安全要求被定義于部署描述符中,并且隨后就被部署者和系統(tǒng)管理員映射到一個(gè)部署環(huán)境中。部署者使用容器部署工具來(lái)處理部署描述符。在運(yùn)行時(shí),容器使用由部署者和系統(tǒng)管理員配置的安全策略來(lái)執(zhí)行授權(quán)。聲明性安全提供了更好的應(yīng)用可移植性,因?yàn)榕c底層容器和操作系統(tǒng)相關(guān)的安全事宜都是在應(yīng)用外部的配置文件中定義的。另外,使用聲明性安全的應(yīng)用更容易開(kāi)發(fā),因?yàn)閷?duì)安全和策略配置事宜的管理是在應(yīng)用的外部進(jìn)行的。應(yīng)用開(kāi)發(fā)者沒(méi)有必要同時(shí)也是一位安全專(zhuān)家,而基于聲明性安全的應(yīng)用也可以更容易地被擴(kuò)充。因此,只要聲明性安全單獨(dú)使用仍然可以充分地描述應(yīng)用的安全需求,那么就要用它來(lái)取代程序性安全。
在程序性安全模型中,應(yīng)用程序員負(fù)責(zé)顯式地編寫(xiě)代碼來(lái)定義和啟用安全。應(yīng)用安全策略是應(yīng)用的一個(gè)完整部分,一個(gè)遵守這個(gè)模型的應(yīng)用被認(rèn)為是識(shí)別安全。程序性安全給應(yīng)用開(kāi)發(fā)造成了更大的困難,并帶來(lái)對(duì)應(yīng)用的可移植性和可擴(kuò)展性的諸多限制,這是因?yàn)榕c指定的應(yīng)用、容器和應(yīng)用所運(yùn)行的操作系統(tǒng)相關(guān)的安全事宜必須被硬編碼。所以,程序性安全僅僅應(yīng)該被使用在那些只使用聲明性安全不足以表達(dá)原因的安全模型的情況。
這兩種安全模型一個(gè)反映的是宏觀一般性的安全策略,一個(gè)反映的是具體實(shí)現(xiàn)上的安全編碼,各自有所側(cè)重。在真正的應(yīng)用安全實(shí)現(xiàn)上,二者應(yīng)該結(jié)合起來(lái)使用。
1. 聲明性安全
HTTP認(rèn)證可以是基本認(rèn)證也可以是摘要認(rèn)證。在基本認(rèn)證中,需要用戶(hù)提供的證書(shū)是用戶(hù)ID和密碼,而且兩者都是以明文傳遞的,關(guān)鍵字為BASIC;摘要認(rèn)證的關(guān)鍵字為DIGEST。
BASIC/DIGEST
SampleAppReal
表單認(rèn)證的關(guān)鍵字是FORM,部署描述符如下:
FORM
/login.html
/login-failed.html
證書(shū)認(rèn)證方式的關(guān)鍵字是CLIENT-CERT,部署描述符如下:
CLIENT-CERT
2. 程序性安全
使用程序性的方法獲得用戶(hù)的身份和特權(quán)信息,需要調(diào)用如:
javax.servlet.http.HttpServletRequest接口中的isUserInRole( )方法,它指示認(rèn)證用戶(hù)(即客戶(hù)端)是否是指定的安全角色的成員。
javax.servlet.http.HttpServletRequest接口中g(shù)etUserPrincipal( )方法,它返回一個(gè)Principal對(duì)象,這個(gè)對(duì)象代表當(dāng)前的認(rèn)證用戶(hù)(客戶(hù)端)。
在基礎(chǔ)Java語(yǔ)言編程中,程序員往往容易掉進(jìn)一些陷阱,這些陷阱是一些邏輯錯(cuò)誤,是程序員對(duì)語(yǔ)言理解不深入造成的。這似乎與安全編碼無(wú)關(guān),但是可以看成是程序員自己在攻擊自己的編碼。所以,本書(shū)需要討論一些*容易被開(kāi)發(fā)者忽視的、但*容易犯的錯(cuò)誤。
2.1 Java編程陷阱
程序陷阱是指那些能夠正常編譯,但是在執(zhí)行時(shí)卻產(chǎn)生事與愿違的,有時(shí)候甚至是災(zāi)難性后果的程序代碼。廣義上任何可能導(dǎo)致程序員把大量的時(shí)間浪費(fèi)在開(kāi)發(fā)工具的使用上而不是*終軟件的進(jìn)展上的語(yǔ)言特性、API或系統(tǒng),都可以稱(chēng)為陷阱。陷阱能夠造成程序員的迷惑,所以要嚴(yán)格分析陷阱。①癥狀或者問(wèn)題,首先找到是哪一個(gè)代碼造成的問(wèn)題,陷阱的類(lèi)型是什么。②問(wèn)題的根源,這個(gè)是揭示陷阱*重要的一個(gè)部分,要深入底層,了解可能導(dǎo)致程序員絆腳的詳細(xì)內(nèi)部工作過(guò)程、無(wú)效的假設(shè)或者API的缺陷。③解決方案,這個(gè)是分析陷阱的*后一個(gè)步驟,*終給出一個(gè)程序?qū)崿F(xiàn)和運(yùn)行結(jié)果。
2.1.1 Java基礎(chǔ)編程陷阱
1. 奇數(shù)誤判
要判斷一個(gè)數(shù)i是不是奇數(shù),有程序員使用了這樣的判別方法:return i % 2==1;,這就產(chǎn)生一個(gè)陷阱。這個(gè)判別式在判斷負(fù)數(shù)的奇偶性時(shí),會(huì)把所有負(fù)整數(shù)判斷為假。這個(gè)陷阱的根源是僅僅考慮了正數(shù)的情況。應(yīng)該改為return i % 2!= 0;。
2. 浮點(diǎn)數(shù)
浮點(diǎn)數(shù)是計(jì)算機(jī)特有的表示數(shù)值的方法,特點(diǎn)是小數(shù)點(diǎn)可以浮動(dòng),這與人理解數(shù)字的方式是不一樣的。例如,System.out.printf(2.0-1.1);顯示出的不是0.9,而是0.8999999999999999,人會(huì)認(rèn)為計(jì)算機(jī)出錯(cuò)了。但是,計(jì)算機(jī)并沒(méi)有出錯(cuò),僅僅是小數(shù)點(diǎn)的浮動(dòng)方式?jīng)]有按照人的想象。如果想得到正確答案0.9,應(yīng)該改為System.out.printf("%.1f", 2.0-1.1),指定小數(shù)點(diǎn)后面只有1位;或者System.out.println(new BigDecimal("2.0").subtract (new BigDecimal("1.1")));。
3. 長(zhǎng)整除
下面的代碼出現(xiàn)的問(wèn)題也是與Java表示數(shù)值的方式有關(guān)的。
……