咱们回忆第一章:一个不安全应用的剖析中,审计人员认为密码以明文形式进行存储是最高优先级的安全风险。实际上,在任何安全系统中,密码安全都是保证已经通过认证的安全实体是真实可靠的重要方面。安全系统的设计人员必须保证密码存储时,任何恶意的用户想要进行破解都是很是困难的。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应用,你会发现数据库中的密码已经进行了编码,登陆功能能够正常使用了。