Springboot整合kaptcha验证码
01、通过配置类来配置kaptcha
01-01、添加kaptcha的依赖:
<!-- kaptcha验证码 -->
<dependency>
<groupId>com.github.penggle</groupId>
<artifactId>kaptcha</artifactId>
<version>2.3.2</version>
</dependency>
02-01、新建KaptchaConfig配置类
package com.czr.config.code;
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
public DefaultKaptcha getDefaultKaptcha(){
DefaultKaptcha captchaProducer = 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", "blue");
properties.setProperty("kaptcha.image.width", "110");
properties.setProperty("kaptcha.image.height", "40");
properties.setProperty("kaptcha.textproducer.font.size", "30");
properties.setProperty("kaptcha.session.key", "code");
properties.setProperty("kaptcha.textproducer.char.length", "4");
properties.setProperty("kaptcha.textproducer.font.names", "宋体,楷体,微软雅黑");
Config config = new Config(properties);
captchaProducer.setConfig(config);
return captchaProducer;
}
}
验证码 KAPTCHA 参数详解
Constant | 描述 | 默认值 |
---|---|---|
kaptcha.border | 图片边框,合法值:yes , no | yes |
kaptcha.border.color | 边框颜色,合法值: r,g,b (and optional alpha) 或者 white,black,blue. | black |
kaptcha.border.thickness | 边框厚度,合法值:>0 | 1 |
kaptcha.image.width | 图片宽 | 200 |
kaptcha.image.height | 图片高 | 50 |
kaptcha.producer.impl | 图片实现类 | com.google.code.kaptcha.impl.DefaultKaptcha |
kaptcha.textproducer.impl | 文本实现类 | com.google.code.kaptcha.text.impl.DefaultTextCreator |
kaptcha.textproducer.char.string | 文本集合,验证码值从此集合中获取 | abcde2345678gfynmnpwx |
kaptcha.textproducer.char.length | 验证码长度 | 5 |
kaptcha.textproducer.font.names | 字体 | Arial, Courier |
kaptcha.textproducer.font.size | 字体大小 | 40px |
kaptcha.textproducer.font.color | 字体颜色,合法值: r,g,b 或者 white,black,blue. | black |
kaptcha.textproducer.char.space | 文字间隔 | 2 |
kaptcha.noise.impl | 干扰实现类 | com.google.code.kaptcha.impl.DefaultNoise |
kaptcha.noise.color | 干扰颜色,合法值: r,g,b 或者 white,black,blue. | black |
kaptcha.obscurificator.impl | 图片样式: 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy | com.google.code.kaptcha.impl.WaterRipple |
kaptcha.background.impl | 背景实现类 | com.google.code.kaptcha.impl.DefaultBackground |
kaptcha.background.clear.from | 背景颜色渐变,开始颜色 | light grey |
kaptcha.background.clear.to | 背景颜色渐变,结束颜色 | white |
kaptcha.word.impl | 文字渲染器 | com.google.code.kaptcha.text.impl.DefaultWordRenderer |
kaptcha.session.key | session key | KAPTCHA_SESSION_KEY |
kaptcha.session.date | session date | KAPTCHA_SESSION_DATE |
03-01、编写controller用于生成验证码:
package com.czr.controller.code;
import com.google.code.kaptcha.Constants;
import com.google.code.kaptcha.Producer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.awt.image.BufferedImage;
@Controller
public class CodeController {
@Autowired
private Producer captchaProducer ;
@RequestMapping("/kaptcha")
public void getKaptchaImage(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpSession session = request.getSession();
response.setDateHeader("Expires", 0);
response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
response.addHeader("Cache-Control", "post-check=0, pre-check=0");
response.setHeader("Pragma", "no-cache");
response.setContentType("image/jpeg");
//生成验证码
String capText = captchaProducer.createText();
session.setAttribute(Constants.KAPTCHA_SESSION_KEY, capText);
//向客户端写出
BufferedImage bi = captchaProducer.createImage(capText);
ServletOutputStream out = response.getOutputStream();
ImageIO.write(bi, "jpg", out);
try {
out.flush();
} finally {
out.close();
}
}
}
04-01、访问验证地址查看验证码
http://localhost:8888/kaptcha
关于验证码的业务对接和注意事项
01、验证码的作用是什么?
- 解决一些程序和肉机攻击的接口的一种防护机制。增加攻击的难度
- 增加一点安全性。
- 短信验证码,语音验证码,图片融合验证码等等
02、原理
通过java的awt的图像技术,然后用随机数(字母和数字的混合)然后生成一张图片,用io流输出到浏览器过程。
03、如何实现防护呢?
- 在生成这个随机数的时候,会在服务器端把生成的随机数放入到session会话中,然后当用户输入验证码的时候,会从session获取比对。
04、登录如何调用验证码的呢?
04-01、在login.html页面增加验证码
<div data-v-7f5e281c="" class="container" >
<!---->
<input data-v-7f5e281c="" placeholder="验证码" id="code" type="text" maxlength="4" v-model="user.code" class="input_init container_error mas-form-input-large" />
<img src="/kaptcha" alt="">
<div data-v-7f5e281c="" class="slot_content"></div>
<div data-v-7f5e281c="" class="error_label" style="color: rgb(224, 95, 66);">
<div data-v-7f5e281c="" class="icon">
<svg data-v-7f5e281c="" width="1em" height="1em" viewbox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg" style="font-size: 12px; transform: rotate(0deg);">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.5 7C12.5 10.0376 10.0376 12.5 7 12.5C3.96243 12.5 1.5 10.0376 1.5 7C1.5 3.96243 3.96243 1.5 7 1.5C10.0376 1.5 12.5 3.96243 12.5 7ZM13.5 7C13.5 10.5899 10.5899 13.5 7 13.5C3.41015 13.5 0.5 10.5899 0.5 7C0.5 3.41015 3.41015 0.5 7 0.5C10.5899 0.5 13.5 3.41015 13.5 7ZM6.0995 3.99504C6.04623 3.46229 6.46459 3 7 3C7.53541 3 7.95377 3.46229 7.9005 3.99504L7.54975 7.50248C7.52151 7.78492 7.28384 8 7 8C6.71616 8 6.47849 7.78492 6.45025 7.50249L6.0995 3.99504ZM7 10.9985C6.44706 10.9985 6 10.5554 6 9.99854C6 9.45308 6.44706 8.99854 7 8.99854C7.55294 8.99854 8 9.44172 8 9.99854C8 10.5554 7.55294 10.9985 7 10.9985Z" fill="currentcolor"></path>
</svg>
</div>
<div data-v-7f5e281c="" class="error_message h5">
请输入验证码
</div>
</div>
</div>
04-02、在login.html页面增加验证码
<img src="/kaptcha" title="看不清,点我改变!!!" onclick="this.src='/kaptcha?d='+new Date().getTime()" alt="">
为什么点击的时候:一定要增加一个随机数或者时间戳呢?
因为:浏览器对图片会进行缓存,因为验证码是一个图片,浏览器默认是根据图片的地址进行缓存的。如果你不加时间戳或者随机数,你点击直接根据地址去缓存中取,所以会拿到缓存图片,就不发生改变。
04-03、增加样式
.ksd-pr{position: relative}
.ksdcode{position: absolute;right:9px;top:6px;border-radius: 12px;}
<div data-v-7f5e281c="" class="container ksdpr" >
<input data-v-7f5e281c="" placeholder="验证码" id="code" type="text" maxlength="4" v-model="user.code" class="input_init container_error mas-form-input-large" />
<img class="ksdcode" src="/kaptcha" title="看不清,点我改变!!!" onclick="this.src='/kaptcha?d='+new Date().getTime()" alt="">
</div>
04-04、业务对接
login.js修改:
var vue = new Vue({
el:"#app",
data:{
title:"MoMo登录",
user:{
account:"10000",
password:"123456",
code:""
}
},
methods:{
toLogin:function(){
var that = this;
var user = that.user;
if(!user.account){
alert("请输入账号")
document.getElementById("account").focus();
return;
}
if(!user.password){
alert("请输入密码")
document.getElementById("pwd").focus();
return;
}
if(!user.code){
alert("请输入验证码")
document.getElementById("code").focus();
return;
}
axios.post("/logined",user).then(function(res){
if(res.data == "success"){
window.location.href = "/";
}else if(res.data=="failcode"){
alert("你的验证码输入有误");
document.getElementById("code").value = "";
document.getElementById("code").focus();
document.getElementById("ksdimgcode").src = "/kaptcha?d="+new Date().getTime();
}else{
alert("输入账号密码有误");
document.getElementById("pwd").value = "";
document.getElementById("pwd").focus();
}
})
}
}
});
后台代码部分:
/**
* 处理登录逻辑
*
* @param userVo
* @param session
* @return
*/
@ResponseBody
@PostMapping("/logined")
public String logined(@RequestBody UserVo userVo, HttpSession session) {
//1: 根据账号去查询用户信息
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(User::getTelephone, userVo.getAccount());
User user = userService.getOne(lambdaQueryWrapper);
// 2: 如果用户存在直接抛异常
if (user == null) {
return "fail";
}
// 3: 比较验证码?
String userinputcode = userVo.getCode();
String sessioncode = (String) session.getAttribute(Constants.KAPTCHA_SESSION_KEY);
// 4:采用equalsIgnoreCase忽略大小写比较,不论你输入大写还小写还是全大写还全小写都可以,只要值相同都相同
boolean codeFlag = userinputcode.equalsIgnoreCase(sessioncode);
if (!codeFlag) {
// 如果不相同,直接返回
return "failcode";
}
// 5:把用户输入的密码进行加密
String inputpwd = MD5Util.md5slat(userVo.getPassword());
// 6:然后把用户输入的密码进行加密和数据库已经加密的密码进行比较,如果一致,就说明是正确的。
if (user.getPassword().equals(inputpwd)) {
// 7: 正确的话,就把登录的用户信息放到session中。
session.setAttribute(KConstants.SESSION_USER, user);
return "success";
}
// 8:不一致用户密码不存在
return "fail";
}