java实现点选汉字验证码
package com.rd.p2p.web; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.geom.AffineTransform; import java.awt.geom.Line2D; import java.awt.image.BufferedImage; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Random; import javax.imageio.ImageIO; import javax.servlet.ServletOutputStream; import org.apache.commons.lang3.StringUtils; import org.apache.poi.ss.formula.functions.T; import org.apache.struts2.convention.annotation.Action; import com.alibaba.fastjson.JSON; import com.rd.p2p.additional.redisCaptcha.util.ResponseUtil; import com.rd.p2p.common.util.redis.RedisValidImgCodeUtils; import com.rd.p2p.core.core.Global; import com.rd.p2p.core.core.constant.Constant; import com.rd.p2p.core.core.web.BaseAction; public class CodeAction extends BaseAction<T> { private static final String KEY = "randomCode"; //点选文字图片验证码刷新次数 private static final String KEY_TOTAL = "select_random_code_total"; //点选文字图片验证码验证通过次数 private static final String KEY_SUCC = "select_random_code_succ"; //点选文字图片验证码验证失败次数 private static final String KEY_FAIL = "select_random_code_fail"; //缓存时间单位秒 设置成7天 private static final int CACHE_SECONDS = 604800; //定义点选文字图片验证码允许的误差值 private static final int ERROR_AMOUNT = 12;// 定义允许的误差值,单位是px //生成汉字的个数 private static Integer[] arr = new Integer[] {1, 2, 3, 4, 5}; //汉字颜色随机范围 private static Color[] colors = {Color.GRAY, Color.LIGHT_GRAY, Color.CYAN}; /** * 跳转页面 * @return */ @Action("/verification") public String verification() { request.setAttribute("web_url", Global.getString("web_url")); return "verification"; } /**生成验证码 * @param src * @param x * @param y */ @Action("/code/getVerificationCode") public void readUsingImageReaderBigcH() { ServletOutputStream outStream = null; try { // http://localhost:8080/gtop/img/149679.jpg //String url = "d:/4.png"; //url = request.getSession().getServletContext().getRealPath("") + url; /* InputStream source = new FileInputStream(src); BufferedImage image = null; image = ImageIO.read(source);*/ //生成背景图片 BufferedImage image = getBackGround(); int hight = image.getHeight(); Graphics graphics = image.getGraphics(); // 设置颜色 graphics.setColor(Color.red); graphics.setFont(new Font("宋体", Font.BOLD, 30)); StringBuilder sb = new StringBuilder(); Random random = new Random(); //转成集合 List<Integer> intList = Arrays.asList(arr); //重新随机排序 Collections.shuffle(intList); //list参数坐标参数 用于校验是否验证通过 List<String> codeList = new ArrayList<String>(); int x = 0; int y = 0; //定义随机1到arr.length某一个字不参与校验 int num = random.nextInt(arr.length)+1; for (int i = 0; i < arr.length; i++) { // 5个汉字,只点4个 String ch = getRandomChineseChar(); int place = intList.get(i); if (place == 1) { x = new Random().nextInt(30) + 40; // 自己定义的位子坐标 y = new Random().nextInt(30) + 40; // i=1的时候,y的值 } if (place == 2) { x = new Random().nextInt(40) + 120; // 自己定义的位子坐标 y = new Random().nextInt(30) + 50; // i=2的时候,y的值 } if (place == 3) { x = new Random().nextInt(70) + 200; // 自己定义的位子坐标 y = new Random().nextInt(50) + 100; // i=3的时候,y的值 } if (place == 4) { x = new Random().nextInt(70) + 80; // i=4的时候,x的值 y = new Random().nextInt(30) + 90; // 自己定义的位子坐标 } if (place == 5) { x = new Random().nextInt(70) + 180; // i=4的时候,x的值 y = new Random().nextInt(30) + 50; // 自己定义的位子坐标 } Constant.LOGGER.info("x:" + x + ",y:" + y + ",hight:" + hight); //字体颜色 graphics.setColor(colors[random.nextInt(colors.length)]); graphics.drawString(ch, x, y); if (place != num) { sb.append(ch); codeList.add(x + "_" + y); // jsp页面坐标原点在字的中间,drawString方法坐标原点在中间 } } Constant.LOGGER.info("汉字:" + sb); //放入session //将产生的随机汉字验证码存进session中进行保存 String sessionid = request.getSession().getId(); RedisValidImgCodeUtils.save(sessionid + KEY, codeList); //增加验证码请求次数 RedisValidImgCodeUtils.increment(KEY_TOTAL, CACHE_SECONDS); // 可以将图片合并传入前端 也可以直接传数据汉字给前端 // 创建顶部图片 BufferedImage bi = new BufferedImage(image.getWidth(), 25, BufferedImage.TYPE_INT_RGB); Graphics gra = bi.getGraphics(); // 设置背景颜色 gra.setColor(Color.WHITE); // 填充区域 gra.fillRect(0, 0, bi.getWidth(), bi.getHeight()); // 设置边框颜色 gra.setColor(Color.BLUE); // 设置边框区域 gra.drawRect(1, 1, bi.getWidth() - 2, bi.getHeight() - 2); // 设置文字背景颜色 Font font = new Font("Microsoft YaHei", Font.BOLD, 16); gra.setFont(font); gra.setColor(Color.BLACK); gra.drawString("按顺序点击:" + sb.toString(), (bi.getWidth() - 10*font.getSize())/2, bi.getHeight()/2 + font.getSize()/2);//设置文字字体 与位子 居中 BufferedImage combined = new BufferedImage(image.getWidth(), image.getHeight() + bi.getHeight(), BufferedImage.TYPE_INT_RGB); Graphics g = combined.getGraphics(); //合并 g.drawImage(bi, 0, 0, null); g.drawImage(image, 0, bi.getHeight(), null); outStream= response.getOutputStream(); ImageIO.write(combined, "jpg", outStream); } catch (IOException e) { e.printStackTrace(); } finally { try { if (outStream != null) { outStream.close(); } } catch (IOException e) { e.printStackTrace(); } } } @Action("/code/verify") public void verify() throws IOException { Map<String, Object> data = new HashMap<String, Object>(); data.put("result", false); String value = request.getParameter("code"); String sessionid = request.getSession().getId(); if (RedisValidImgCodeUtils.get(sessionid + KEY) == null) { printWebJson(JSON.toJSONString(data)); return; } List<String> sValue = (List<String>) RedisValidImgCodeUtils.get(sessionid + KEY); //取到数据后直接清掉redis RedisValidImgCodeUtils.del(sessionid + KEY); Constant.LOGGER.info("**前端请求数据***"+value); Constant.LOGGER.info("**后端实际数据**"+sValue.toString()); //为null 或者"" 或者 " " if (StringUtils.isBlank(value) || sValue == null || sValue.size() < 1) { printWebJson(JSON.toJSONString(data)); return; } String [] valueStr = value.split(","); if(valueStr.length != sValue.size() || valueStr.length != 4){ printWebJson(JSON.toJSONString(data)); return; } /*判断坐标参数是否正确*/ String str = ""; for (int i = 0; i < valueStr.length; i++) { str = valueStr[i].toString(); if(StringUtils.isBlank(str) || StringUtils.isBlank(sValue.get(i).toString())){ printWebJson(JSON.toJSONString(data)); return; } String [] vL = valueStr[i].toString().split("_"); String [] svL = sValue.get(i).toString().split("_"); if(vL.length != svL.length || svL.length != 2){ printWebJson(JSON.toJSONString(data)); return; } //x轴 y轴判断 坐标点在左上角 ,图片宽度30px 点击范围扩大12px, 范围在 x-13 < x <x+13 ; if(!(Integer.parseInt(svL[0])-ERROR_AMOUNT < Integer.parseInt(vL[0])-15 && Integer.parseInt(vL[0])-15 < Integer.parseInt(svL[0])+ERROR_AMOUNT ) || !(Integer.parseInt(svL[1])-ERROR_AMOUNT < Integer.parseInt(vL[1])-15 && Integer.parseInt(vL[1])-15 < Integer.parseInt(svL[1])+ERROR_AMOUNT)){ //增加验证失败次数 RedisValidImgCodeUtils.increment(KEY_FAIL, CACHE_SECONDS); printWebJson(JSON.toJSONString(data)); return; } } //增加验证通过次数 RedisValidImgCodeUtils.increment(KEY_SUCC, CACHE_SECONDS); data.put("result", true); printWebJson(JSON.toJSONString(data)); } /** * 生成背景图片 * @return */ public BufferedImage getBackGround(){ int width=300; //指定生成验证码的宽度 int height=200; //指定生成验证码的高度 BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); Graphics g = image.getGraphics(); Graphics2D g2d = (Graphics2D)g; //创建Graphics2D对象 Random random = new Random(); // Font mFont = new Font("黑体", Font.BOLD, 16); //定义字体样式 // g.setColor(getRandColor(200, 250)); //背景色 g.fillRect(0, 0, width, height); //绘制背景 // g.setFont(mFont); //设置字体 g.setColor(getRandColor(180, 200)); //线条色 //绘制88根位置和颜色全部为随机产生的线条,该线条为2f for (int i = 0; i < 88; i++) { int x = random.nextInt(width-1); int y = random.nextInt(height-1); int x1 = random.nextInt(100)+1; int y1 = random.nextInt(120)+1; BasicStroke bs = new BasicStroke(2f,BasicStroke.CAP_BUTT,BasicStroke.JOIN_BEVEL); Line2D line = new Line2D.Double(x,y,x+x1,y+y1); g2d.setStroke(bs); g2d.draw(line); //绘制直线 // g2d.setColor(getRandColor(random, 30, 150)); //随机每条线条的颜色 } //输出生成的验证码图片 g.dispose(); return image; } private static Color getRandColor(Random random, int fc, int bc){ if (fc > 255) fc = 255; if (bc > 255) bc = 255; int r = fc + random.nextInt(bc - fc); int g = fc + random.nextInt(bc - fc); int b = fc + random.nextInt(bc - fc); return new Color(r, g, b); } private static String[] generateCheckCode() { String[] res = new String[2]; Random random = new Random(); int intTemp; int intFirst = random.nextInt(100); int intSec = random.nextInt(100); String checkCode = ""; int result = 0; switch (random.nextInt(6)) { case 0: if (intFirst < intSec) { intTemp = intFirst; intFirst = intSec; intSec = intTemp; } checkCode = intFirst + " - " + intSec + " = ?"; result = intFirst-intSec; break; case 1: if (intFirst < intSec) { intTemp = intFirst; intFirst = intSec; intSec = intTemp; } checkCode = intFirst + " - ? = "+(intFirst-intSec); result = intSec; break; case 2: if (intFirst < intSec) { intTemp = intFirst; intFirst = intSec; intSec = intTemp; } checkCode = "? - "+intSec+" = "+(intFirst-intSec); result = intFirst; break; case 3: checkCode = intFirst + " + " + intSec + " = ?"; result = intFirst + intSec; break; case 4: checkCode = intFirst + " + ? ="+(intFirst+intSec); result = intSec; break; case 5: checkCode = "? + " + intSec + " ="+(intFirst+intSec); result = intFirst; break; } res[0] = checkCode; res[1] = String.valueOf(result); Constant.LOGGER.info("result=" + result); return res; } @Action("/code/calc") public void calcCode() throws IOException{ int width = 140, height = 37; try { BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); Graphics g = image.getGraphics(); Random random = new Random(); g.setColor(getRandColor(random, 200, 250)); g.fillRect(0, 0, width, height); String[] fontTypes = { "\u5b8b\u4f53", "\u65b0\u5b8b\u4f53", "\u9ed1\u4f53", "\u6977\u4f53", "\u96b6\u4e66" }; int fontTypesLength = fontTypes.length; g.setColor(getRandColor(random, 160, 200)); g.setFont(new Font("Times New Roman", Font.PLAIN, 14 + random.nextInt(6))); for (int i = 0; i < 255; i++) { int x = random.nextInt(width); int y = random.nextInt(height); int xl = random.nextInt(12); int yl = random.nextInt(12); g.drawLine(x, y, x + xl, y + yl); } String[] result = generateCheckCode(); RedisValidImgCodeUtils.save(request.getSession().getId() + "calc_code", result[1]); String [] baseChar = result[0].split(" "); for (int i = 0; i < baseChar.length; i++) { g.setColor(getRandColor(random, 30, 150)); g.setFont(new Font(fontTypes[random.nextInt(fontTypesLength)], Font.BOLD, 22 + random.nextInt(6))); g.drawString(baseChar[i], 24 * i + 10, 24); } g.dispose(); //发送图片 ResponseUtil.sendImg(response, image, "image/jpeg", "code", "jpg"); } catch (IllegalStateException e) { Constant.LOGGER.error(e.getMessage()); e.printStackTrace(); } } @Action("/code/getRandomCode") public void getRandomCode() throws IOException{ // TODO Auto-generated method stub //设置不缓存图片 response.setHeader("Pragma", "No-cache"); response.setHeader("Cache-Control", "No-cache"); response.setDateHeader("Expires", 0); //指定生成的响应图片 response.setContentType("image/jpeg"); int width=140; //指定生成验证码的宽度 int height=37; //指定生成验证码的高度 BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); Graphics g = image.getGraphics(); Graphics2D g2d = (Graphics2D)g; //创建Graphics2D对象 Random random = new Random(); Font mFont = new Font("黑体", Font.BOLD, 22); //定义字体样式 g.setColor(getRandColor(200, 250)); g.fillRect(0, 0, width, height); //绘制背景 g.setFont(mFont); //设置字体 g.setColor(getRandColor(180, 200)); //绘制100根位置和颜色全部为随机产生的线条,该线条为2f for (int i = 0; i < 100; i++) { int x = random.nextInt(width-1); int y = random.nextInt(height-1); int x1 = random.nextInt(6)+1; int y1 = random.nextInt(12)+1; BasicStroke bs = new BasicStroke(2f,BasicStroke.CAP_BUTT,BasicStroke.JOIN_BEVEL); Line2D line = new Line2D.Double(x,y,x+x1,y+y1); g2d.setStroke(bs); g2d.draw(line); //绘制直线 } //输出由英文,数字和中文随机组成的验证文字,具体的组合方式根据生成随机数确定 String sRand = ""; //输出随机的验证文字 String ctmp = ""; int itmp = 0; for(int i = 0;i<4;i++){ switch (random.nextInt(2)) { case 0: itmp = random.nextInt(26)+65; //生成A~Z的字母 ctmp = String.valueOf((char)itmp); break; default: ctmp = String.valueOf(random.nextInt(8)+2); //生成2~9的数字 break; } sRand+=ctmp; Color color = new Color(20+random.nextInt(110), 20+random.nextInt(110), 20+random.nextInt(110)); g.setColor(color); //将生成的随机数进行随机缩放病旋转指定角度 //将文字旋转指定角度 Graphics2D g2d_word = (Graphics2D)g; AffineTransform trans = new AffineTransform(); trans.rotate(random.nextInt(45)*3.14/180, 15*i+8, 7); //缩放文字 /*float scaleSize = random.nextFloat()+0.8f; if(scaleSize > 1f){ scaleSize = 1f; } trans.scale(scaleSize, scaleSize); */ g2d_word.setTransform(trans); g.drawString(ctmp, 20*i+18, 18); //每个字的间距xy } //将生成的验证码保存道session中 RedisValidImgCodeUtils.save(request.getSession().getId() + "randCheckCode", sRand); //输出生成的验证码图片 g.dispose(); ImageIO.write(image, "JPEG", response.getOutputStream()); } public Color getRandColor(int s,int e){ Random random = new Random(); if(s>255)s = 255; if(e>255)e = 255; int r = s+random.nextInt(e-s); int g = s+random.nextInt(e-s); int b = s+random.nextInt(e-s); return new Color(r, g, b); } public static String getRandomChineseChar() { String str = null; int hs, ls; Random random = new Random(); hs = (176 + Math.abs(random.nextInt(39))); ls = (161 + Math.abs(random.nextInt(93))); byte[] b = new byte[2]; b[0] = (new Integer(hs).byteValue()); b[1] = (new Integer(ls).byteValue()); try { str = new String(b, "GBk"); //转成中文 } catch (UnsupportedEncodingException ex) { ex.printStackTrace(); } return str; } }
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta name="viewport" content="width=device-width,initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"/> <script src="${web_url}/api/themes/theme_default/media/js/jquery.js"></script> <title>验证码</title> </head> <body> <div style="text-align:center;position:relative;"> <!--<h2>这是点击的验证码</h2> --> <!-- 这是点击的验证码 --> <img id="codeT3" src="${web_url}/api/code/getVerificationCode.html?flag=" +Math.random()"/> </br> <!-- <input type="button" value="刷新" onclick="getCodeTree();" /> <input type="button" value="校验" onclick="cheakOutTree();" /> --> <select id="codeSelect" style="display: none;"></select> <img src="${web_url}/api/refresh.png" style="position:absolute;right:0;top:0; width:20px; height: 20px;" onclick="getCodeTree();" /> </div> <script type="text/javascript"> //点击次数 var number=0; //获取验证码3 function getCodeTree() { number = 0; $(".zhezhao").remove(); document.getElementById("codeSelect").options.length = 0; $("#codeT3").attr("src","${web_url}/api/code/getVerificationCode.html?flag="+Math.random()); } $(function() { $("#codeT3").bind("click", function(ev) { var oEvent = ev || event; //var number = $("#codeSelect option").length; number++; if (number > 4) { return; } var x = oEvent.pageX; var y = oEvent.pageY; var img = document.getElementById('codeT3'); //获取图片的原点 var nodex = getNodePosition(img)[0];//原点x 与原点y var nodey = getNodePosition(img)[1]; var xserver = parseInt(x) - parseInt(nodex); var yserver = parseInt(y) - parseInt(nodey); $("#codeSelect").append( "<option value='"+ (parseInt(number)+1) +"'>" + xserver + "_" + yserver + "</option>"); var oDiv = document.createElement('img'); oDiv.style.left = (parseInt(x)-15) + 'px'; // 指定创建的DIV在文档中距离左侧的位置 图片大小30 左右移动5 oDiv.style.top = (parseInt(y) -15) + 'px'; // 指定创建的DIV在文档中距离顶部的位置 oDiv.style.border = '1px solid #FF0000'; // 设置边框 oDiv.style.position = 'absolute'; // 为新创建的DIV指定绝对定位 oDiv.style.width = '30px'; // 指定宽度 oDiv.style.height = '30px'; // 指定高度 //oDiv.src = 'select.png'; oDiv.style.opacity = '0.5'; //透明度 oDiv.className = 'zhezhao';//加class 点刷新后删除遮罩 document.body.appendChild(oDiv); //第四次点击后自动提交 if (number == 4) { cheakOutTree(); } }); }) //校验验证码 function cheakOutTree() { var txt = ""; $("#codeSelect option").each(function (){ var text = $(this).text(); if(txt == ""){ txt = text; }else{ txt = txt + "," + text; } }); $.ajax({ type:"post", url:"${web_url}/api/code/verify.html", data : {"code" : txt}, cache : false, success : function(data) { alert(data.result); if (!data.result) { getCodeTree(); } } }); } function getNodePosition(node) { var top = left = 0; while (node) { if (node.tagName) { top = top + node.offsetTop; left = left + node.offsetLeft; node = node.offsetParent; } else { node = node.parentNode; } } return [left, top]; } </script> </body> </html>
本文来自博客园,作者:小珍珠在河里敲代码,转载请注明原文链接:https://www.cnblogs.com/Jansens520/p/8473360.html