述
上文中实现了验证码的发送,先后的发送的代码是这样的
这两个发送的方法其实都是一样的. 只是最后一个是返回图片给页面,一个是给用户发送验证码, 那这两个方法其实可以抽象出来,封装成一个, 下面看一下这些逻辑该如何去重构
验证码发送器
图片验证码或者短信验证码的逻辑如下:
- 生成一个验证码
- 放到缓存里面去
- 返回给用户
他们其实可以封装成以下三个方法
- 生成验证码对象的方法,这里返回的可能是短信验证码对象,也可能是图片验证码对象
- 放到缓存里面, 不同的验证码的key是不一样的
- 发送给用户, 图片时直接返回给页面, 然后短信是调用第三方的接口
这里就可以抽象出来,首先,写一个接口, ValidateCodeProcessor
:
1 | public interface ValidateCodeProcessor { |
通过这个 createCode
方法里面把上面的三步都实现了, 但是第三步 返回给用户,这个步骤是不一样的,所以这个第三步我们可以写一个抽象的方法,交给各个子类去实现
抽象实现
首先得有一个抽象类实现上面的接口, AbstractValidateCodeProcessor
:
1 | @Slf4j |
这个代码比较多,首先来看一下,我们上面注入了一个 private Map<String, ValidateCodeGenerator> validateCodeGenerators;
这样的一个Map, 这个的作用就是, Spring 会查询容器里面的,所有的 ValidateCodeGenerator
的实现, 然后注入到这个Map里面, key 的值首先会取 @Compent("")
注解里面指定的名字,如果没有的话,就是默认的
在我们现在的项目中, ValidateCodeGenerator
的实现有两个, 一个是图片验证生成器,一个是短信验证码生成器
所以容器启动后,这个Map里面会有这两个实现
然后再看一下方法, 首先是 createCode()
方法,这里面就是我们上面的三个步骤
然后再具体看下里面的这三个方法
验证码生成方法
generate()
方法
这里首先用到了一个 getValidateCodeType()
方法, 我们后面controller中验证码的接口会合并成一个,通过前端传类型来判断是什么请求, 所以这里就是通过请求来判断是哪种类型的验证码,返回是个枚举类型 ValidateCodeType
代码如下:
1 | public enum ValidateCodeType implements Serializable { |
前端请求是/code/{type}
,然后传 sms ,或者 image 两种
然后我们上面的Map会把所有的验证码生成器加载到, 图片的生成器key是 imageCodeGenerator
短信的是 smsCodeGenerator
所以这里就可以通过前端传过来的 type 去决定用哪种类型的生成器了
取到生成器后调用生成方法,返回
放入缓存方法
saveCache()
,这个没什么好多说的, 用到了一个 getRedisKey()
方法, 这里根据不同的验证码类型写不同的key
最后放到缓存里面就可以了
发送方法
send()
这个方法只写了一个抽象的方法,具体去由子类的发送器去实现, 所以我们就还需要两个发送的实现类
发送功能的实现
首先是图片的发送类, 新建 ImageCodeProcessor
然后继承 AbstractValidateCodeProcessor
重写发送的方法,代码如下:
1 | @Component("imageCodeProcessor") |
再建一个短信的发送器,代码如下:
1 | @Component("smsCodeProcessor") |
接口修改
现在发送验证码的逻辑封装完了,controller中的接口就可以合并成一个了,代码如下:
1 | @RestController |
首先是注入了一个Map,收集所有的 ValidateCodeProcessor
就是验证码发送器,然后调用发送的方法
配置类修改
最后 BrowserSecurityConfig
中之要把接口 code/{type}
加入到不需要权限里面去, 之前配置了 /code/image
改成 /code/*
就好了
验证逻辑
上面的修改完成之后, 之前的 ValidateCodeFilter
可能会报错, 所以先注释掉 validate()
方法,先保证验证码可以成功发送
测试
启动项目,访问登录页, 然后发一个验证码,控制台看一下输出,如下:
发送逻辑就都ok了, 关于验证的逻辑之后再做