背景
注册-登录-修改密码?般需要发送验证码,但是容易被攻击恶意调?
什么是短信-邮箱轰炸机
?机短信轰炸机是批、循环给?机?限发送各种?站的注册验证码短信的?法。
公司带来的损失
短信?条5分钱,如果被?盗刷?家??计算邮箱通知不?钱,但被?盗刷,带宽、接等都被占?,导致?法正常使?
如何避免??的?站成为”?鸡“或者被刷呢?
增加图形验证码(开发?员) 单IP请求次数限制(开发?员) 限制号码发送(?般短信提供商会做)
攻防永远是有的,只过加?了攻击者的成本,ROI划不过来?然就放弃了
Kaptcha 框架介绍
?歌开源的?个可?度配置的实?验证码?成?具 验证码的字体/??/颜? 验证码内容的范围(数字,字?,中?汉字!) 验证码图?的??,边框,边框粗细,边框颜? 验证码的?扰线 验证码的样式(?眼样式、3D、普通模糊)
添加Kaptcha依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>kaptcha-spring-boot-starter</artifactId>
<version>1.0.0</version>
</dependency>
Kaptcha配置
import com.google.code.kaptcha.Constants;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.code.kaptcha.util.Config;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Properties;
@Configuration
public class KaptchaConfig {
@Bean
@Qualifier("kaptchaProducer")
public DefaultKaptcha kaptcha() {
DefaultKaptcha kaptcha = new DefaultKaptcha();
Properties properties = new Properties();
properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "4");
properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_CHAR_SPACE,"8");
properties.setProperty(Constants.KAPTCHA_NOISE_IMPL, "com.google.code.kaptcha.impl.NoNoise");
properties.setProperty(Constants.KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.WaterRipple");
properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_CHAR_STRING, "0123456789");
Config config = new Config(properties);
kaptcha.setConfig(config);
return kaptcha;
}
}
CommonUtil?具类
import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.MessageDigest;
public class CommonUtil {
public static String getIpAddr(HttpServletRequest request) {
String ipAddress = null;
try {
ipAddress = request.getHeader("x-forwarded-for");
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("WL-Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getRemoteAddr();
if (ipAddress.equals("127.0.0.1")) {
InetAddress inet = null;
try {
inet = InetAddress.getLocalHost();
} catch (UnknownHostException e) {
e.printStackTrace();
}
ipAddress = inet.getHostAddress();
}
}
if (ipAddress != null && ipAddress.length() > 15) {
if (ipAddress.indexOf(",") > 0) {
ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
}
}
} catch (Exception e) {
ipAddress="";
}
return ipAddress;
}
public static String MD5(String data) {
try {
java.security.MessageDigest md = MessageDigest.getInstance("MD5");
byte[] array = md.digest(data.getBytes("UTF-8"));
StringBuilder sb = new StringBuilder();
for (byte item : array) {
sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
}
return sb.toString().toUpperCase();
} catch (Exception exception) {
}
return null;
}
}
接?开发
import com.google.code.kaptcha.Producer;
import net.xdclass.xdclassredis.util.CommonUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.util.concurrent.TimeUnit;
@RestController
@RequestMapping("/api/v1/kaptcha")
public class KaptchaController {
@Autowired
private RedisTemplate<String,Object> redisTemplate;
@Autowired
private Producer kaptchaProducer;
@GetMapping("get_kaptcha")
public void getKaptcha(HttpServletRequest request, HttpServletResponse response){
String KaptchaText = KaptchaProducer.createText();
String key = getKaptchaKey(request);
redisTemplate.opsForValue().set(key,kaptchaText,10,TimeUnit.MINUTES);
BufferedImage bufferedImage = kaptchaProducer.createImage(kaptchaText);
ServletOutputStream outputStream = null;
try {
outputStream = response.getOutputStream();
ImageIO.write(bufferedImage,"jpg",outputStream);
outputStream.flush();
outputStream.close();
}catch (Exception e){
e.printStackTrace();
}
}
private String getKaptchaKey(HttpServletRequest request){
String ip = CommonUtil.getIpAddr(request);
String userAgent = request.getHeader("User-Agent");
String key = "user-service:kaptcha:"+CommonUtil.MD5(ip+userAgent);
return key;
}
}
JsonData响应?具类封装
public class JsonData {
private Integer code;
private Object data;
private String msg;
public JsonData(int code,Object data,String msg){
this.code = code;
this.msg = msg;
this.data = data;
}
public static JsonData buildSuccess() {
return new JsonData(0, null, null);
}
public static JsonData buildSuccess(Object data) {
return new JsonData(0, data, null);
}
public static JsonData buildError(String msg) {
return new JsonData(-1, null, msg);
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
校验逻辑
import com.google.code.kaptcha.Producer;
import net.xdclass.xdclassredis.util.CommonUtil;
import net.xdclass.xdclassredis.util.JsonData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.util.concurrent.TimeUnit;
@RestController
@RequestMapping("/api/v1/kaptcha")
public class KaptchaController {
@Autowired
private RedisTemplate<String,Object> redisTemplate;
@Autowired
private Producer kaptchaProducer;
@GetMapping("get_kaptcha")
public void getKaptcha(HttpServletRequest request, HttpServletResponse response){
String kaptchaText = kaptchaProducer.createText();
String key = getKaptchaKey(request);
redisTemplate.opsForValue().set(key,kaptchaText,10,TimeUnit.MINUTES);
BufferedImage bufferedImage = kaptchaProducer.createImage(kaptchaText);
ServletOutputStream outputStream = null;
try {
outputStream = response.getOutputStream();
ImageIO.write(bufferedImage,"jpg",outputStream);
outputStream.flush();
outputStream.close();
}catch (Exception e){
e.printStackTrace();
}
}
@GetMapping("send_code")
public JsonData sendCode(@RequestParam(value = "to",required = true)String to,
@RequestParam(value = "kaptcha",required = true) String kaptcha,
HttpServletRequest request){
String key = getKaptchaKey(request);
String cacheKaptcha = redisTemplate.opsForValue().get(key);
if(kaptcha!=null && cacheKaptcha!=null && cacheKaptcha.equalsIgnoreCase(kaptcha)){
redisTemplate.delete(key);
return JsonData.buildSuccess();
}else {
return JsonData.buildError("验证码错误");
}
}
private String getKaptchaKey(HttpServletRequest request){
String ip = CommonUtil.getIpAddr(request);
String userAgent = request.getHeader("User-Agent");
String key = "user-service:captcha:"+CommonUtil.MD5(ip+userAgent);
return key;
}
}
|