SPRING与设计模式----适配器与外观模式
适配器模式将一个类的接口,转换成客户期望的另一个接口。适配器让原来接口不兼容的类可以合作无间。适配器充满良好的OO设计原则:使用对象组合,以修改接口包装被适配者。实际应用场景:旧系统改造,适应新系统的接口。
外观facade模式提供了一个统一的接口,用来访问子系统中的一群接口。外观定义了一个高层接口,让子系统更容易使用。实际应用场景:新旧系统兼容改造,对访问者简化接口的调用。
实际案例:springsecurity4的PasswordEncoder接口的变动,为了兼容原有代码,DaoAuthenticationProvider生成匿名内部来进行新旧接口的适配。
背景:
authentication.encoding.PasswordEncoder,为了避免撞库破解密码,使用了salt来进行密码混淆;
crypto.password.PasswordEncoder 新的接口不再需要salt,就可以保证每次加密后的内容是不一致的,避免了撞库破解。默认推荐的实现类BCryptPasswordEncoder
UML类图:
PasswordEncoder适配器实现代码 ,见DaoAuthenticationProvider源码:
/**
* Sets the PasswordEncoder instance to be used to encode and validate passwords. If
* not set, the password will be compared as plain text.
* <p>
* For systems which are already using salted password which are encoded with a
* previous release, the encoder should be of type
* {@code org.springframework.security.authentication.encoding.PasswordEncoder}.
* Otherwise, the recommended approach is to use
* {@code org.springframework.security.crypto.password.PasswordEncoder}.
*
* @param passwordEncoder must be an instance of one of the {@code PasswordEncoder}
* types.
*/
public void setPasswordEncoder(Object passwordEncoder) {
Assert.notNull(passwordEncoder, "passwordEncoder cannot be null");
if (passwordEncoder instanceof PasswordEncoder) {
setPasswordEncoder((PasswordEncoder) passwordEncoder);
return;
}
if (passwordEncoder instanceof org.springframework.security.crypto.password.PasswordEncoder) {
final org.springframework.security.crypto.password.PasswordEncoder delegate = (org.springframework.security.crypto.password.PasswordEncoder) passwordEncoder;
setPasswordEncoder(new PasswordEncoder() {
public String encodePassword(String rawPass, Object salt) {
checkSalt(salt);
return delegate.encode(rawPass);
}
public boolean isPasswordValid(String encPass, String rawPass, Object salt) {
checkSalt(salt);
return delegate.matches(rawPass, encPass);
}
private void checkSalt(Object salt) {
Assert.isNull(salt,
"Salt value must be null when used with crypto module PasswordEncoder");
}
});
return;
}
throw new IllegalArgumentException(
"passwordEncoder must be a PasswordEncoder instance");
}