kaptcha的定义就不再细说了,可以到官方文档中查看,有详细的说明,包括详细配置等。
- 使用kaptcha需要导入依赖jar包,版本可以根据自己需要查询仓库自己定义,如下:
<dependency>
<groupId>com.github.penggle</groupId>
<artifactId>kaptcha</artifactId>
<version>2.3.2</version>
</dependency>
- 定义一个captcha配置类,如下
import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.code.kaptcha.util.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Properties;
@Configuration
public class KaptchaConfig {
@Bean(name="kaptcha")
public DefaultKaptcha kaptcha(){
DefaultKaptcha kaptcha = new DefaultKaptcha();
Properties properties = new Properties();
//设置验证码
//设置边框
properties.setProperty("kaptcha.border", "yes");
//设置边框颜色
properties.setProperty("kaptcha.border.color", "105,179,90");
// 字体颜色
properties.setProperty("kaptcha.textproducer.font.color", "black");
// 图片宽
properties.setProperty("kaptcha.image.width", "145");
// 图片高
properties.setProperty("kaptcha.image.height", "40");
// 字体大小
properties.setProperty("kaptcha.textproducer.font.size", "30");
// session key
properties.setProperty("kaptcha.session.key", "code");
// 验证码长度
properties.setProperty("kaptcha.textproducer.char.length", "5");
// 字体
properties.setProperty("kaptcha.textproducer.font.names", "宋体,楷体,微软雅黑");
Config config = new Config(properties);
kaptcha.setConfig(config);
return kaptcha;
}
}
- 定义获取验证码的请求接口,生成验证码,如下
//此处注入的bean名称需要和KaptchaConfig中的bean名称一致
@Autowired
private Producer kaptcha;
@RequestMapping(value = "/captcha.jpg",method = RequestMethod.GET)
public void captcha(HttpServletRequest request, HttpServletResponse response) throws IOException {
//用字节数组存储
byte[] captchaChallengeAsJpeg = null;
ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream();
ServletOutputStream responseOutputStream = response.getOutputStream();
final HttpSession httpSession=request.getSession();
try {
//生产验证码字符串并保存到session中
String createText = kaptcha.createText();
//打印随机生成的字母和数字
log.debug(createText);
httpSession.setAttribute(Constants.KAPTCHA_SESSION_KEY, createText);
//使用生产的验证码字符串返回一个BufferedImage对象并转为byte写入到byte数组中
BufferedImage challenge = kaptcha.createImage(createText);
ImageIO.write(challenge, "jpg", jpegOutputStream);
captchaChallengeAsJpeg = jpegOutputStream.toByteArray();
response.setHeader("Cache-Control", "no-store");
response.setHeader("Pragma", "no-cache");
response.setDateHeader("Expires", 0);
response.setContentType("image/jpeg");
//定义response输出类型为image/jpeg类型,使用response输出流输出图片的byte数组
responseOutputStream.write(captchaChallengeAsJpeg);
responseOutputStream.flush();
} catch (IllegalArgumentException | IOException e) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}finally {
responseOutputStream.close();
}
}
- 前端页面使用vue+element实现,如下
<el-form-item label="验证码" :label-width="formLabelWidth">
<el-row>
<el-col :span="14">
<el-input v-model="form.kaptchaCode" autocomplete="off" @keyup.enter.native="onLogin" placeholder="点击图片刷新"></el-input>
</el-col>
<el-col :span="10">
<img class="pointer" :src="src" @click="refreshCode">
<!-- <a href="javascript:;" @click="refreshCode">点击刷新</a> -->
</el-col>
</el-row>
</el-form-item>
然后需要定义一个src变量,先设置为空,src的值需要调用后端的生成验证码接口来获取,如下
this.global.baseUrl是前端请求后端的路径地址。
refreshCode(){
this.src = this.global.baseUrl + "/captcha.jpg?t=" + new Date().getTime();
}
如此即可生成验证码如下:
- 验证码校验,可以写一个filter进行拦截校验
public class CaptchaValidateFilter extends AccessControlFilter{
/**
* 是否开启验证码
*/
private boolean captchaEnabled = true;
/**
* 验证码类型
*/
private String captchaType = "math";
public void setCaptchaEnabled(boolean captchaEnabled){
this.captchaEnabled = captchaEnabled;
}
public void setCaptchaType(String captchaType){
this.captchaType = captchaType;
}
@Override
public boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception{
request.setAttribute(ShiroConstants.CURRENT_ENABLED, captchaEnabled);
request.setAttribute(ShiroConstants.CURRENT_TYPE, captchaType);
return super.onPreHandle(request, response, mappedValue);
}
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue)
throws Exception{
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
// 验证码禁用 或不是表单提交 允许访问
if (captchaEnabled == false || !"post".equals(httpServletRequest.getMethod().toLowerCase())){
return true;
}
return validateResponse(httpServletRequest, httpServletRequest.getParameter(ShiroConstants.CURRENT_VALIDATECODE));
}
public boolean validateResponse(HttpServletRequest request, String validateCode){
Object obj = ShiroUtils.getSession().getAttribute(Constants.KAPTCHA_SESSION_KEY);
String code = String.valueOf(obj != null ? obj : "");
// 验证码清除,防止多次使用。
request.getSession().removeAttribute(Constants.KAPTCHA_SESSION_KEY);
if (StringUtils.isEmpty(validateCode) || !validateCode.equalsIgnoreCase(code)){
return false;
}
return true;
}
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception{
request.setAttribute(ShiroConstants.CURRENT_CAPTCHA, ShiroConstants.CAPTCHA_ERROR);
return true;
}
}
需要注意的是,这里请求需要是同一个请求或者说是同一个线程,否则可能会出现获取不到验证码的问题。