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>