一、什么是图片验证码?
可以参考下面这张图:
我们在一些网站注册的时候,经常需要填写以上图片的信息。
这种图片验证方式是我们最常见的形式,它可以有效的防范恶意攻击者采用恶意工具,调用“动态验证码短信获取”接口进行动态短信发送, 导致接入用户短信被刷,造成账号余额损失。同时这种动态发送方式会朝许多无关的手机用户,发送很多验证码短信,导致手机用户被骚扰,甚至引起用户投诉。这种恶意攻击究其原因是攻击者可以自动对接口进行大量调用。
如果网站在用户进行“动态验证码短信发送” 操作前,要求用户输入图片验证码,确认用户是真实有效后,服务器端再发送动态短信到用户手机上。这一种流程就可以有效的解决恶意攻击问题。
正确的加入图片验证码的方式是在短信验证码发送前,先让用户填写图片验证码,再发送短信验证码。
举一个正确的例子(下图)
说了这么多,具体是怎么实现的呢?
一、验证码生成类
package hbi.tech.utils; import javax.imageio.ImageIO; import java.awt.*; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.Random; /** * 验证码生成器 * */ public class SCaptcha { // 图片的宽度。 private int width = 120; // 图片的高度。 private int height = 40; // 验证码字符个数 private int codeCount = 4; // 验证码干扰线数 private int lineCount = 50; // 验证码 private String code = null; // 验证码图片Buffer private BufferedImage buffImg = null; private char[] codeSequence = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '2', '3', '4', '5', '6', '7', '8', '9' }; // 生成随机数 private Random random = new Random(); public SCaptcha() { this.createCode(); } /** * * @param width 图片宽 * @param height 图片高 */ public SCaptcha(int width, int height) { this.width = width; this.height = height; this.createCode(); } /** * * @param width 图片宽 * @param height 图片高 * @param codeCount 字符个数 * @param lineCount 干扰线条数 */ public SCaptcha(int width, int height, int codeCount, int lineCount) { this.width = width; this.height = height; this.codeCount = codeCount; this.lineCount = lineCount; this.createCode(); } public void createCode() { int codeX = 0; int fontHeight = 0; fontHeight = height - 5;// 字体的高度 codeX = width / (codeCount + 3);// 每个字符的宽度 // 图像buffer buffImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); Graphics2D g = buffImg.createGraphics(); // 将图像填充为白色 g.setColor(Color.WHITE); g.fillRect(0, 0, width, height); // 创建字体 ImgFontByte imgFont = new ImgFontByte(); Font font = imgFont.getFont(fontHeight); g.setFont(font); StringBuffer randomCode = new StringBuffer(); // 随机产生验证码字符 for (int i = 0; i < codeCount; i++) { String strRand = String.valueOf(codeSequence[random.nextInt(codeSequence.length)]); // 设置字体颜色 g.setColor(getRandomColor()); // 设置字体位置 g.drawString(strRand, (i + 1) * codeX, getRandomNumber(height / 2) + 25); randomCode.append(strRand); } code = randomCode.toString(); } /** 获取随机颜色 */ private Color getRandomColor() { int r = getRandomNumber(255); int g = getRandomNumber(255); int b = getRandomNumber(255); return new Color(r, g, b); } /** 获取随机数 */ private int getRandomNumber(int number) { return random.nextInt(number); } public void write(String path) throws IOException { OutputStream sos = new FileOutputStream(path); this.write(sos); } public void write(OutputStream sos) throws IOException { ImageIO.write(buffImg, "png", sos); sos.close(); } public BufferedImage getBuffImg() { return buffImg; } public String getCode() { return code; } /** 字体样式类 */ class ImgFontByte { public Font getFont(int fontHeight) { try { Font baseFont = Font.createFont(Font.HANGING_BASELINE, new ByteArrayInputStream( hex2byte(getFontByteStr()))); return baseFont.deriveFont(Font.PLAIN, fontHeight); } catch (Exception e) { return new Font("Arial", Font.PLAIN, fontHeight); } } private byte[] hex2byte(String str) { if (str == null) return null; str = str.trim(); int len = str.length(); if (len == 0 || len % 2 == 1) return null; byte[] b = new byte[len / 2]; try { for (int i = 0; i < str.length(); i += 2) { b[i / 2] = (byte) Integer.decode("0x" + str.substring(i, i + 2)).intValue(); } return b; } catch (Exception e) { return null; } } // 字体文件的十六进制字符串 private String getFontByteStr() { //防止报字符串长度过长错误,改为从配置文件读取 return ReadFontByteProperties.getFontByteStr(); } } }
二、读取字体文件类
package hbi.tech.utils; import java.io.InputStream; import java.util.Properties; public class ReadFontByteProperties { static private String fontByteStr = null; static { loads(); } synchronized static public void loads() { if (fontByteStr == null) { InputStream is = ReadFontByteProperties.class.getResourceAsStream("/fontByte.properties"); Properties dbproperties = new Properties(); try { dbproperties.load(is); fontByteStr = dbproperties.getProperty("fontByteStr").toString(); } catch (Exception e) { //System.err.println("不能读取属性文件. " + "请确保fontByte.properties在CLASSPATH指定的路径中"); } } } public static String getFontByteStr() { if (fontByteStr == null) loads(); return fontByteStr; } }
三、生成验证码接口
/** * @author jiaqing.xu@hand-china.com * @date 2017/8/23 * @description 生成图片验证码 */ @RequestMapping(value = "/userInfo/verification", method = {RequestMethod.POST, RequestMethod.GET}) @ResponseBody public void verification(HttpServletRequest request, HttpServletResponse response, HttpSession session) throws IOException { // 设置响应的类型格式为图片格式 response.setContentType("image/jpeg"); // 禁止图像缓存。 response.setHeader("Pragma", "no-cache"); response.setHeader("Cache-Control", "no-cache"); response.setDateHeader("Expires", 0); //实例生成验证码对象 SCaptcha instance = new SCaptcha(); //将验证码存入session session.setAttribute("verification", instance.getCode()); //向页面输出验证码图片 instance.write(response.getOutputStream()); }
将生成的验证码图片存在session中,当用户登录时即可和用户输入的验证码的值进行判断,如果验证相同,则进行后续操作。
原文链接:https://www.qiquanji.com/post/8484.html
本站声明:网站内容来源于网络,如有侵权,请联系我们,我们将及时处理。
微信扫码关注
更新实时通知