短信登录配置及重构
重构思路:
-
重构不是更改已有的功能
-
重构是不影响已有功能的情况下,对已有代码进行抽象封装
-
多处使用相同代码的地方,需要抽出来
-
比如上章节的很多代码,
如:图形验证码过滤器和短信验证码过滤器重复代码太多
服务接口的url地址和过滤器中的过滤器地址重复
等..
系统配置相关的代码结构
core项目中的重构如下:
- 密码登录的配置代码
- 短信登录的配置代码
- 验证码相关的配置代码
browser项目:
-
BrowserSecurityConfig
-
只留下浏览器特有的配置代码
- 如记住我的功能,只有浏览器特有这样的功能
app:
- AppSecurityConfig
- App特有的配置代码
通过配置apply功能进行配置的引用
感受
花了6个小时看老师重构之后的代码,然后完成了自己跟练的项目代码;
太厉害!!这个重构技巧太牛逼了;
总之:当有两处重复代码的时候 就要抽取代码了。这个需要大量的经验才能不分类好,不至于越抽越乱
这里再啰嗦下:
关于用户名密码登录和短信登录表单提交的url地址,不需要真实存在,
因为这个是提供这两个特定过滤器框架特定的拦截点。只有提交到指定的拦截点,
才会进入认证功能服务
此次重构一些知识点
- 善用 HttpSecurity.apply 应用分离之后的配置类
- 程序中有手动写字符串2次的就抽成 SecurityConstants 常量接口类
- 善用 Autowired注解提供的 依赖查找功能
- 善用 类名统一起名
- 善用枚举类 提供相应的支持
善用 HttpSecurity.apply 应用分离之后的配置类
HttpSecurity.apply 方法跟踪进来是父类的;这里是一个泛型,所有需要看HttpSecurity对应传递的是什么类型
public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBuilder<O>>
extends AbstractSecurityBuilder<O> {
public <C extends SecurityConfigurerAdapter<O, B>> C apply(C configurer)
throws Exception {
configurer.addObjectPostProcessor(objectPostProcessor);
configurer.setBuilder((B) this);
add(configurer);
return configurer;
}
--------- HttpSecurity 声明
public final class HttpSecurity extends
AbstractConfiguredSecurityBuilder<DefaultSecurityFilterChain, HttpSecurity>
implements SecurityBuilder<DefaultSecurityFilterChain>,
HttpSecurityBuilder<HttpSecurity> {
------------ 注意看对比
public <C extends SecurityConfigurerAdapter<O, B>> C apply(C configurer
AbstractConfiguredSecurityBuilder<DefaultSecurityFilterChain, HttpSecurity>
这里的c对应的泛型就是 <O, B> ,而O,B对应到HttpSecurity的声明就是<DefaultSecurityFilterChain, HttpSecurity>
HttpSecurity.apply 返回一个SecurityConfigurerAdapter<O, B>,所以这里只要继承该类,就是apply需要的对象了
------------- 如下示例
/**
* 验证码配置
* @author : zhuqiang
* @version : V1.0
* @date : 2018/8/5 20:05
*/
@Component
public class ValidateCodeSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
/**
* @see ValidateCodeFilter 目前融合了短信和图形验证码的验证功能
*/
@Autowired
private Filter validateCodeFilter;
@Override
public void configure(HttpSecurity http) throws Exception {
// 由源码得知,在最前面的是UsernamePasswordAuthenticationFilter
http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class);
}
}
SecurityConstants 常量接口类
在重构中就已经发现这样做的好处了。因为这个代码被分离写和配置的,老是忘记在哪些地方用过
在修改的时候经常忘记修改,或则找不到,所以需要抽成常量类
public interface SecurityConstants {
/**
* 默认的处理验证码的url前缀
*/
public static final String DEFAULT_VALIDATE_CODE_URL_PREFIX = "/code";
善用 Autowired注解提供的 依赖查找功能
把多个实现类统一管理,特别是使用模板方法抽取公用逻辑的时候,就拍上用处了
如下代码,还提供了按beanName查找指定的子类实现;
还提供了按自定义类型,下面会讲到善用命名会在某些地方起到奇效
/**
* 处理器持有者,用来管理所有验证码类型的处理器
* @author : zhuqiang
* @version : V1.0
* @date : 2018/8/5 20:40
*/
@Component
public class ValidateCodeProcessorHolder {
@Autowired
private Map<String, ValidateCodeProcessor> validateCodeProcessors;
public ValidateCodeProcessor findValidateCodeProcessor(ValidateCodeType type) {
return findValidateCodeProcessor(type.toString().toLowerCase());
}
public ValidateCodeProcessor findValidateCodeProcessor(String type) {
String beanName = type.toLowerCase() + ValidateCodeProcessor.class.getSimpleName();
ValidateCodeProcessor processor = validateCodeProcessors.get(beanName);
if (processor == null) {
throw new ValidateCodeException("验证码处理器 " + beanName + " 不存在");
}
return processor;
}
}
善用 类名统一起名
如这里的几个类
- ValidateCodeProcessor 验证码处理接口
- ImageValidateCodeProcessor 图片验证码处理接口
- SmsValidateCodeProcessor 短信验证码处理接口
这里的前缀,配合上面的技巧 善用 Autowired注解提供的 依赖查找功能,使用以下代码就能方便的获取到对应的处理器
他们都一个共同的父类,有公用的步骤,变化的部分由子类实现;
public abstract class AbstractValidateCodeProcessor<C extends ValidateCode> implements ValidateCodeProcessor {
/**
* 根据请求的url获取校验码的类型:
* ValidateCodeProcessorHolder : 中持有所有本类的子类型,获取getClass能拿到具体的实例类名
* @return
* @see ValidateCodeProcessorHolder
*/
private ValidateCodeType getValidateCodeType() {
// 处理器 命名规则:ImageValidateCodeProcessor,拿到前缀即可
// 返回 Image
String type = StringUtils.substringBefore(getClass().getSimpleName(), ValidateCodeProcessor.class.getSimpleName());
return ValidateCodeType.valueOf(type.toUpperCase());
}
}
在外部使用 type + ValidateCodeProcessor.class.getSimpleName() 就能获取到完整的类名,
也就能使用ValidateCodeProcessorHolder动态的获取处理器了
善用枚举类提供相应的支持
枚举类的名称是 短信和图片验证功能的前缀。配合上面的几条。
在使用模板方法模式抽取公用逻辑的时候,可以使用前缀获取不同功能支持的动态常量等类容
在外部要动态使用服务的时候,也能用前缀+具体的的父类命名获取到
public enum ValidateCodeType {
/**
* 短信验证码
*/
SMS {
@Override
public String getParamNameOnValidate() {
return SecurityConstants.DEFAULT_PARAMETER_NAME_CODE_SMS;
}
},
/**
* 图片验证码
*/
IMAGE {
@Override
public String getParamNameOnValidate() {
return SecurityConstants.DEFAULT_PARAMETER_NAME_CODE_IMAGE;
}
};
/**
* 校验时从请求中获取的参数的名字
* @return
*/
public abstract String getParamNameOnValidate();
}