【造个*系列】之自定义验证码的 springboot starter
前言
springboot很好用的原因之一就是它本身封装了好多starter,拿来就可以用,不用像SSM那样配置好多。所以,我们也可以自定义一个starter,造个小*,自己用。我就以验证码为例子。
一、工厂模式
验证码的样式多种多样,有静态的PNG格式的,有动态的GIF格式的。还有它的高度、宽度,字符串的长度也都可以改变。所以我们可以使用java设计模式中的工厂模式来生成不同的验证码。
工厂模式参考:https://www.runoob.com/design-pattern/factory-pattern.html。看完这篇,你就发现,工厂模式就这么简单。?
二、定义验证码属性
为了让验证码的属性可以配置,我们就需要写一个配置类来封装一下这些乱七八糟的属性。
1、先来一个枚举,定义验证码是静态的还是动态的。
public enum CaptchaType {
/**
*静态图片验证码
*/
PNG,
/**
*动态GIF格式的验证码
*/
GIF;
}
2、再来一个枚举,定义验证码里字符串的类型
public enum CharTypeEnum {
TYPE_DEFAULT(1,"数字和字母混合"),
TYPE_ONLY_NUMBER(2,"纯数字"),
TYPE_ONLY_CHAR(3,"纯字母"),
TYPE_ONLY_UPPER(4,"纯大写字母"),
TYPE_ONLY_LOWER(5,"纯小写字母"),
TYPE_NUM_AND_UPPER(6,"数字和大写字母");
private Integer code;
private String charType;
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getCharType() {
return charType;
}
public void setCharType(String charType) {
this.charType = charType;
}
private CharTypeEnum(Integer code, String charType) {
this.code = code;
this.charType = charType;
}
}
3、把这两个枚举放进配置类里就可以了。这些属性最好设置一下默认值,这样在使用的时候,不做任何配置也是可以正常使用的。
@ConfigurationProperties("java.captcha") 这个注解的意思就是定义application.properties里配置项的。
@ConfigurationProperties("java.captcha")
public class CaptchaProperties {
//验证码图片的宽度
private Integer width = 130;
//验证码图片的高度
private Integer height = 48;
//验证码图片的位数
private Integer length = 4;
//验证码图片的字符类型
private CharTypeEnum charType = CharTypeEnum.TYPE_DEFAULT;
//生成的验证码图片类型
private CaptchaType captchaType = CaptchaType.PNG;
//缓存的名称
private String captchaKey = "captcha";
//验证码过期时间,默认为5分钟
private int expireTimeInSeconds = 300;
public Integer getWidth() {
return width;
}
public void setWidth(Integer width) {
this.width = width;
}
public Integer getHeight() {
return height;
}
public void setHeight(Integer height) {
this.height = height;
}
public Integer getLength() {
return length;
}
public void setLength(Integer length) {
this.length = length;
}
public CharTypeEnum getCharType() {
return charType;
}
public void setCharType(CharTypeEnum charType) {
this.charType = charType;
}
public CaptchaType getCaptchaType() {
return captchaType;
}
public void setCaptchaType(CaptchaType captchaType) {
this.captchaType = captchaType;
}
public String getCaptchaKey() {
return captchaKey;
}
public void setCaptchaKey(String captchaKey) {
this.captchaKey = captchaKey;
}
public int getExpireTimeInSeconds() {
return expireTimeInSeconds;
}
public void setExpireTimeInSeconds(int expireTimeInSeconds) {
this.expireTimeInSeconds = expireTimeInSeconds;
}
}
三、验证码生成
1、定义一个工厂类,根据传入的不同的参数,生成不同的验证码。
为了更加通用性,我打算不仅可以通过 application.properties 来配置验证码的属性,而且又能通过传参来改变验证码的属性。这时,我们就需要ServletRequestUtils这个工具类,从HttpServletRequest来拿参数了,如果拿不到的话就用配置的属性。
public class CaptchaGenerateFactory {
public static Captcha getCaptcha(CaptchaProperties properties,ServletWebRequest request){
boolean isGif = ServletRequestUtils.getBooleanParameter(request.getRequest(),"gif",false);
Integer width = ServletRequestUtils.getIntParameter(request.getRequest(), "width",properties.getWidth());
Integer height = ServletRequestUtils.getIntParameter(request.getRequest(), "height",properties.getHeight());
Integer length = ServletRequestUtils.getIntParameter(request.getRequest(), "length",properties.getLength());
if (isGif) {
return new GifCaptcha(width,height,length);
}else{
if (Objects.equals(properties.getCaptchaType(), CaptchaType.PNG)) {
return new SpecCaptcha(width,height,length);
}
if (Objects.equals(properties.getCaptchaType(),CaptchaType.GIF)) {
return new GifCaptcha(width,height,length);
}
}
return null;
}
}
2、生成验证码
先定义一个接口,这个接口很简单,就一个生成方法。Captcha 我使用了一个别人写的工具类,依赖如下。我没用谷歌的,主要是它直接扔session里了,最主要的是太丑了!!!
public interface CaptchaService {
Captcha generate(ServletWebRequest request);
}
<dependency>
<groupId>com.github.whvcse</groupId>
<artifactId>EasyCaptcha</artifactId>
<version>1.5.0</version>
</dependency>
然后实现这个接口,注入上面的配置类,调用工厂类来生成验证码。生成验证码的功能基本就实现了。但是你有没有发现这个实现类没有@Service注解,这个再下一步会讲到。
public class CaptchaServiceImpl implements CaptchaService{
@Autowired
private CaptchaProperties properties;
/**
* 生成图形验证码
* @return
*/
public Captcha generate(ServletWebRequest request){
Captcha captcha = CaptchaGenerateFactory.getCaptcha(properties,request);
//设置类型
captcha.setCharType(properties.getCharType().getCode());
return captcha;
}
}
四、starter配置
功能已经实现了,但是这些还不够,直接引入的话,是不起作用的。还需要一些配置,就是告诉springboot一声:“喂,我写了个starter,你得加载!”。
@Configuration这个注解常见,表明这是个配置类,spring来管理。
@EnableConfigurationProperties这个注解就是启用我们写好的配置类。
@Bean注解和@Service的结果是一样的,都是告诉spring你来管理这个类吧。
@ConditionalOnMissingBean这个注解大概意思就是你要是引用这个starter了,要是没实现CaptchaService这个接口,那就用我的实现CaptchaServiceImpl,要是你实现了CaptchaService这个接口,那就用你的!spring也不会管理CaptchaServiceImpl这这个类了。
@Configuration
@EnableConfigurationProperties({CaptchaProperties.class})
public class CaptchaAutoConfiguration {
@Bean
@ConditionalOnMissingBean(CaptchaService.class)
public CaptchaService captchaService(){
return new CaptchaServiceImpl();
}
}
做完这些,还是不行。你就这么想,spring实在是太笨了,就算你配置好了,但是,它找不到啊!!!所以,我们还得给它做个目录之类的,指引它找到。
在src/main/resources目录下新建META-INF文件夹,文件夹里新建spring.factories文件,然后写点东西告诉这个笨笨的spring去哪路找我们写好的配置。第一行最后的 \ 的意思就是换行的意思。第二行就是上面那个类的包位置。
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
work.waynelee.captcha.CaptchaAutoConfiguration
五、使用
都写完了,该怎么用呢??使用也是非常简单的。这些为啥不在starter里写呢?既然拿到验证码里的字符串了,你爱存哪里存哪里呗。再说了,也不是谁的业务逻辑都一样,剩下的,想怎么搞怎么搞呗,像是验证码失效的什么的就自己解决呗。
@Autowired
private CaptchaService captchaService;
@Autowired
private CaptchaProperties properties;
public void getCaptcha(HttpServletRequest request,HttpServletResponse response) throws IOException{
CaptchaUtil.setHeader(response);
Captcha captcha = captchaService.generate(new ServletWebRequest(request));
String text = captcha.text().toLowerCase();
request.getSession().setAttribute(properties.getCaptchaKey(),text);
captcha.out(response.getOutputStream());
}
然后你也可以在application.properties里配置你想改变的属性,就像下面这样
java.captcha.captcha-type=png
你要不想配置也没啥事,请求的时候传参数就可以了。默认的就是这样的。
想变成GIF格式的?简单啊,加个参数就是了。
想变长?没问题!
就这样。
写在最后的话
感觉没啥可说的,不过推荐一个工具吧,https://jitpack.io/。按我的理解啊,就是一个第三方私服,挺好用的。有空再说怎么用吧。