【目录】
- 背景
- 关于NoOpPasswordEncoding
- 其它Pasword Encoder
- 关于DelegatingPasswordEncoding
- 收获
0. 背景
在自定义配置Spring Security时,你或许会注意到这段代码。
@Bean
public static PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder().getInstance();
}
代码中提到的NoOpPasswordEncoder是什么?
关于NoOpPasswordEncoder
字面理解它是一个某某密码编译器(No Operate Password Encoder,即:无任何操作的密码密码编译器)。
看一下NoOpPasswordEncoder类的代码,通过注释了解到,该类已经弃用且不安全。它并不会对密码做什么操作,只是为了测试为目的的。也就是说在encode、matches过程中密码都是明文的。
所以在Spring文档中见到它,不足为奇了,因为它容易理解,但是真的在项目中使用Spring Security时不应该是NoOpPasswordEncoder.
其它Password Encoder
NoOpPasswordEncoder的注释中推荐我们有针对性的选择使用BCryptPasswordEncoder、Pbkdf2PasswordEncoder、SCryptPasswordEncoder中的一种。并推荐最好使用DelegatingPasswordEncoder,因为它支持密码升级。
暂时可以先这样理解:前三种都是根据某个算法对PasswordEncoder接口的具体实现,DelegatingPasswordEncoder是N种具体算法的代理实现。
通过这几个类的注释了解到BCryptPasswordEncoder、Pbkdf2PasswordEncoder、SCryptPasswordEncoder都是基于某种算法的PasswordEncoder接口的实现类。使用时需要根据需要,有选择的使用其中的一种即可。
比如我们想使用BCrypt算法来对密码加密,我们需要指定使用的PasswordEncoder是BCryptPasswordEncoder:
@Bean
public static PasswordEncoder passwordEncoder () {
return new BCryptPasswordEncoder();
}
在设定密码的,我们需要手动对明文的密码进行encode:
String password = "123456";
String encodedPassword = new BCryptPasswordEncoder().encode(password)
然后将encodedPassword密码进行保存。
如果我的项目开始选择A算法,后面改为B算法怎么办?如果A算法encode之后生成的密码是不可逆的,那么就自己编写这样一个逻辑:验证时先用A算法encode并match,成功后在用B算法encode并保存。好的事情Spring Security提供了现成的功能,那就是DelegatingPasswordEncoder。
关于DelegatingPasswordEncoder
DelegatingPasswordEncoder是N种算法实现的代理,通过DelegatingPasswordEncoder的实例,我们能够使用指定的算法进行encode,同时支持对旧的密码进行match。
你可以通过DelegatingPasswordEncoder的构造函数来实例化它:
String idForEncode = "bcrypt"; // 默认使用的encode算法的id
Map<String, PasswordEncoder> encoders = new HashMap<>();
encoders.put(idForEncoder, new BCryptPasswordEncoder());
encoders.put("noop", NoOpPasswordEncoder.getInstance());
encoders.put("pykdf2", new Pbkdf2PasswordEncoder());
encoders.put("scrypt", new SCryptPasswordEncoder());
encoders.put("sha256", new StandardPasswordEncoder());
PasswordEncoder passwordEncoder = new DelegatingPasswordEncoder(idForEncoder, encoders);
也可以使用PasswordEncoderFactories来获取DelegatingPasswordEncoder的一个实例。
PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
要使用DelegatingPasswordEncoder,还需要了解一下3件事情:
- 生成的密码结构
- 如何利用密码结构来进行密码匹配
- 如何指定当前要用的加密算法
下面逐一说明:
1,生成的密码结构如下:
{算法ID}加密后的内容
例如:
{bcrypt}$2a$10$lkbmWQlpkNpuVoYas5ZKl.zP.4F21IWeQAL4SSL.aEIb036o6vp3G
{}中的bcrypt就是算法的ID
{}后面的字符串就是加密后的字符串
2,如何利用密码结构来进行密码匹配?
如果你使用A算法加密,生成的密码是
{A}adfadfadsfsdf
那么DelegatingPasswordEncoder就会根据算法ID({}中的值)来获取当前密码中使用的算法A,并用算法A对密码进行加密后在匹配。
如果原本使用A算法,后来改进使用了B算法,那么在DelegatingPasswordEncoder实例中添加B算法后。对旧密码(使用A算法生成的密码),通过算法ID,知道旧密码使用了A算法,并用A算法去加密匹配。对于新的密码,则直接使用B算法来进行加密、匹配。这样就支持了算法的升级。
3,如何指定当前使用哪种算法?
在DelegatingPasswordEncoder的有餐构造函数中,我们可以传递两个值来构造DelegatingPasswordEncoder的实例,如下:
new DelegatingPasswordEncoder(idForEncoder, encoders);
idForEncoder:DelegatingPasswordEncoder默认使用的算法ID
encoders:DelegatingPasswordEncoder中包含哪些PasswordEncoder
收获
通过浏览Spring Security中的PasswordEncoder代码,最终了解了PasswordEncoder这种密码升级的解决方案的一种良好的代码实现。
【Final】
建议阅读下面的参考,获取想要了解的更多细节。
【参考】