咱們回憶第一章:一個不安全應用的剖析中,審計人員認爲密碼以明文形式進行存儲是最高優先級的安全風險。實際上,在任何安全系統中,密碼安全都是保證已經通過認證的安全實體是真實可靠的重要方面。安全系統的設計人員必須保證密碼存儲時,任何惡意的用戶想要進行破解都是很是困難的。java
在數據庫存儲時,須要遵照如下的準則:web
密碼不能以明文的形式進行存儲(簡單文本);算法
用戶提供的密碼必須與數據庫存儲的密碼進行比較;spring
密碼不能應用戶的請求提供(即便用戶忘記了密碼)。sql
對於大多數應用來講,爲了知足以上要求最適合的方式是對密碼進行單向的編碼或加密。單向編碼提供了安全和惟一的特性,這對於正確的認證用戶很是重要,它可以保證密碼一旦被加密就不能再被解密了。數據庫
在大多數的安全應用設計中,通常沒有必要和實際要求根據用戶的請求檢索用戶的密碼,由於若是沒有附加的安全認證,提供用戶的實際密碼會有很大的安全風險。做爲替代方案,大多數的應用爲用戶提供了重設密碼的功能,要麼須要提供額外的認證信息(如社會保險號碼、生日、繳稅ID或其它我的信息),要麼經過一個基於email的系統。bootstrap
【存儲其它類型的敏感信息:如下的準則能夠應用於密碼以及其它類型的敏感信息,包括社會保險號以及信用卡信息(儘管基於應用的不一樣,它們中的一些須要解密的能力)。比較常見的是數據庫以多種形式來存儲這些敏感信息,好比用戶16個數字的信用卡數字能夠用一種高度加密的形式存儲,可是最後的四位數字以明文的形式存儲(做爲佐證,能夠回憶一些商業站點會顯示XXXX XXXX XXXX 1234來幫助你分別已存儲的信用卡)。】安全
基於咱們(實際上並不太符合現實)使用SQL來訪問HSQL數據庫中用戶的環境,你可能已經在思考如何對密碼進行編碼。HSQL以及其它大多數的數據庫並無提供加密方法做爲數據庫的內置功能。ide
通常來講,bootstrap過程(爲系統添加初始的用戶和數據)會聯合使用一些SQL加載和Java代碼。根據應用的複雜性,這個過程也可能會變得很複雜。編碼
對於JBCP Pets應用來講,咱們將會繼續使用嵌入式數據庫聲明和對應的SQL,而且會添加一點Java代碼在初始化加載後執行來加密數據庫中的全部密碼。爲了使得密碼加密可以正常工做,兩個過程必須同步的使用密碼加密以確保密碼可以被一致的處理和校驗。
在Spring Security中,密碼加密已經進行了封裝,經過o.s.s.authentication.encoding.PasswordEncoder接口的實現類來定義。經過使用<authentication-provider>元素裏的<password-encoder>聲明咱們可以很容易地配置密碼編碼:
<authentication-manager alias="authenticationManager"> <authentication-provider user-service-ref="jdbcUserService"> <password-encoder hash="sha"/> </authentication-provider> </authentication-manager>
你可能會很高興的瞭解到Spring Security已經提供了一系列PasswordEncoder的實現,它們能夠用於不一樣的需求和安全須要。要使用哪一個實現能夠經過<password-encoder>元素的hash屬性來指定。
如下的列表列出了內置的實現類以及它們的優勢。這些實現類都在o.s.s.authentication.Encoding包下。
與Spring Security其餘領域同樣,能夠引用一個PasswordEncoder的實現類以提供更精確的配置,並容許PasswordEncoder經過依賴注入織入到其它的bean中。對於JBCP Pets來講,咱們須要使用這個bean引用的方法來編碼用戶的初始數據。
讓咱們瞭解一下爲JBCP Pet應用配置基本密碼編碼的過程。
配置密碼編碼
配置基本的密碼編碼涉及到兩個地方——在咱們的SQL腳本執行後,加密載入數據庫中數據的密碼,並確保DaoAuthenticationProvider被配置成使用PasswordEncoder。
首先,做爲一般的Spring bean,聲明一個PasswordEncoder的實例
<bean class="org.springframework.security.authentication. encoding.ShaPasswordEncoder" id="passwordEncoder"/>
你會發現咱們使用了SHA-1的PasswordEncoder實現。這是一個高效的單向加密算法,在密碼存儲中常常用到。
咱們須要配置DaoAuthenticationProvider來持有一個對PasswordEncoder的引用,這樣它就能夠在用戶登陸時,編碼並比較用戶提供的密碼。添加<password-encoder>聲明並指向咱們在前面定義的bean的ID:
<authentication-manager alias="authenticationManager"> <authentication-provider user-service-ref="jdbcUserService"> <password-encoder ref="passwordEncoder"/> </authentication-provider> </authentication-manager>
若是在此時重啓應用,並嘗試登陸,你會發現前面合法的登陸憑證如今都會被拒絕。這是由於數據庫中存儲的密碼(在啓動時經過test-users-groups-data.sql腳本加載的)並無以加密的形式存儲。咱們須要用一些java代碼對啓動數據進行後置的處理。
咱們對SQL加載的數據進行編碼的方式是使用了Spring bean的初始化方法,它將在embedded-database bean實例化完成後執行。這個bean, com.packtpub.springsecurity.security.DatabasePasswordSecurerBean很簡單:
public class DatabasePasswordSecurerBean extends JdbcDaoSupport { @Autowired private PasswordEncoder passwordEncoder; public void secureDatabase() { getJdbcTemplate().query("select username, password from users", new RowCallbackHandler(){ @Override public void processRow(ResultSet rs) throws SQLException { String username = rs.getString(1); String password = rs.getString(2); String encodedPassword = passwordEncoder.encodePassword(password, null); getJdbcTemplate().update("update users set password = ? where username = ?", encodedPassword,username); logger.debug("Updating password for username: "+username+" to: "+encodedPassword); } }); } }
代碼使用了JdbcTemplate功能來遍歷全部的數據庫中全部的用戶並使用注入的PasswordEncoder引用對密碼進行編碼。每個密碼都進行了更新。
咱們須要配置這個Spring bean的聲明以使其在web應用啓動時及<embedded-database>初始化後再進行該類的初始化。Spring bean的依賴跟蹤機制保證DatabasePasswordSecurerBean可以在合適的時機執行:
若是你此時重啓JBCP Pets應用,你會發現數據庫中的密碼已經進行了編碼,登陸功能能夠正常使用了。