上一章简单的处理了密码加密问题。但密码加密中有一个更为棘手的问题,这便是加密算法的升级。
如果系统设计之初,还使用md5
加密,之后改为了更安全的sha1
或sha256
,再之后又要改为pbkdf2
或bcrypt
之类的现代算法。如果又出现更好的加密技术,或者原有加密算法被发现有漏洞,还得继续更改。
这种更改在已上线的系统中是不可承受的,因为一旦改动加密算法,所有用户的密码都会失效。没有谁可以承受这样的后果。有一些经验丰富的技术人员会使用一些复杂的手段,让加密算法升级变得可行。但Spring Security提供了一种代价极低、无感的方式实现这一目标。
基本原理是,提供一系列的加密算法,如md5
sha1
sha256
pbkdf2
bcrypt
等,并将其中一个作为默认算法。在加密的时候,将加密算法名称放到密码字段里,这样就知道当前的密码使用的是什么算法。如:
{bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG
{pbkdf2}5d923b44a6d129f3ddf3e3c8d29412723dcbde72445e8ef6bf3b508fbf17fa4ed4d6b99ca763d8dc
{scrypt}$e0801$8bWJaSu2IKSn9Z9kM+TPXfOc/9bdYSrN1oD9qfVThWEwdRTnO7re7Ei+fUZRJ68k9lTyuTeUp4of4g24hHnazw==$OAOec05+bXxvuu/1qZ6NUR+xQYvYv7BeL1QxwRpY5Pc=
{sha256}97cde38028ad898ebc02e690819fa220e88c62e0699403e94fff291cfffaf8410849f27605abcbc0
如系统需要升级算法,只需要将默认算法改为新算法。用户登录时,可以使用老算法进行匹配,如果匹配成功,则自动使用新算法对用户密码进行升级。
比如系统之前使用的是sha256
,密码用户的密码为:
{sha256}97cde38028ad898ebc02e690819fa220e88c62e0699403e94fff291cfffaf8410849f27605abcbc0
之后使用bcrypt
作为新的默认算法。该用户进行登录时,使用sha256
算法进行加密匹配。一旦匹配成功,在用户登录成功的同时,使用新的默认算法bcrypt
对用户密码进行加密,并写入数据库。该用户数据库的密码就变成了:
{bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG
这样就再也不怕升级加密算法了,对用户完全无感;对开发人员而言,也只需改一两行代码;如果完全使用Spring Security的方案,则一行代码都不用改,只需升级Spring Security版本即可。
这么方便,那么配置会不会很复杂呢?简单到不能更简单:
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
或者自定义加密算法:
@Bean
public PasswordEncoder passwordEncoder() {
String idForEncode = "bcrypt";
Map encoders = new HashMap<>();
encoders.put(idForEncode, new BCryptPasswordEncoder());
encoders.put("noop", NoOpPasswordEncoder.getInstance());
encoders.put("pbkdf2", new Pbkdf2PasswordEncoder());
encoders.put("scrypt", new SCryptPasswordEncoder());
encoders.put("sha256", new StandardPasswordEncoder());
PasswordEncoder passwordEncoder = new DelegatingPasswordEncoder(idForEncode, encoders);
}
另外还需实现修改密码的接口UserDetailsPasswordService
@Bean
public UserDetailsPasswordService userDetailsPasswordService() {
return new UserDetailsPasswordService() {
@Override
public UserDetails updatePassword(UserDetails user, String newPassword) {
...
}
};
}
如果说之前还对使用Spring Security有些犹豫的话,这个设计则完全坚定了我使用Spring Security的决心。单纯为了这个功能使用Spring Security都不亏。