WebService/src/main/java/com/java110/web/components/ValidateCodeComponent.java
@@ -28,6 +28,22 @@ } } /** * 校验验证码 * @param pd * @return */ public ResponseEntity<String> validate(IPageData pd){ ResponseEntity<String> responseEntity = null; try{ responseEntity = loginServiceSMOImpl.validate(pd); }catch (Exception e){ responseEntity = new ResponseEntity<String>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR); }finally { return responseEntity; } } public ILoginServiceSMO getLoginServiceSMOImpl() { return loginServiceSMOImpl; WebService/src/main/java/com/java110/web/core/VueComponentElement.java
@@ -1,5 +1,9 @@ package com.java110.web.core; import com.alibaba.fastjson.JSONObject; import com.java110.web.smo.impl.LoginServiceSMOImpl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.thymeleaf.Arguments; import org.thymeleaf.dom.*; import org.thymeleaf.processor.element.AbstractMarkupSubstitutionElementProcessor; @@ -13,12 +17,17 @@ */ public class VueComponentElement extends AbstractMarkupSubstitutionElementProcessor { private final static Logger logger = LoggerFactory.getLogger(VueComponentElement.class); protected VueComponentElement(String elementName) { super(elementName); } @Override protected List<Node> getMarkupSubstitutes(Arguments arguments, Element element) { //logger.debug("arg:{},element:{}", JSONObject.toJSONString(arguments),element.getAttributeValue("name")); List<Node> nodes = new ArrayList<>(); //获取模板名称 String componentName = element.getAttributeValue("name"); @@ -27,6 +36,7 @@ throw new RuntimeException("在缓存中未找到组件【"+componentName+"】"); } Node nodeHtml = new Macro(html); nodes.add(nodeHtml); //css String css = VueComponentTemplate.findTemplateByComponentCode(componentName+"."+VueComponentTemplate.COMPONENT_CSS); @@ -44,6 +54,7 @@ nodes.add(nodeJs); } return nodes; } WebService/src/main/java/com/java110/web/smo/ILoginServiceSMO.java
@@ -22,4 +22,11 @@ * @return */ public ResponseEntity<String> generateValidateCode(IPageData pd); /** * 验证码校验 * @param pd 页面请求对象 * @return */ public ResponseEntity<String> validate(IPageData pd); } WebService/src/main/java/com/java110/web/smo/impl/LoginServiceSMOImpl.java
@@ -1,12 +1,14 @@ package com.java110.web.smo.impl; import com.alibaba.fastjson.JSONObject; import com.java110.common.cache.CommonCache; import com.java110.common.constant.CommonConstant; import com.java110.common.constant.ServiceConstant; import com.java110.common.util.Assert; import com.java110.core.base.smo.BaseServiceSMO; import com.java110.core.context.IPageData; import com.java110.core.factory.AuthenticationFactory; import com.java110.core.factory.ValidateCodeFactory; import com.java110.web.smo.ILoginServiceSMO; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -66,29 +68,46 @@ */ @Override public ResponseEntity<String> generateValidateCode(IPageData pd) { int w = 200, h = 80; String verifyCode = ValidateCodeFactory.generateVerifyCode(4); ResponseEntity<String> verifyCodeImage = null; try { verifyCodeImage = new ResponseEntity<>(ValidateCodeFactory.outputImage(200, 80, verifyCode), HttpStatus.OK); //将验证码存入Redis中 CommonCache.setValue(pd.getSessionId()+"_validateCode",verifyCode,CommonCache.defaultExpireTime); BufferedImage image = new BufferedImage(IMG_WIDTH, IMG_HEIGHT, BufferedImage.TYPE_INT_RGB); // 实例化BufferedImage Graphics g = image.getGraphics(); Color c = new Color(200, 200, 255); // 验证码图片的背景颜色 g.setColor(c); g.fillRect(0, 0, IMG_WIDTH, IMG_HEIGHT); // 图片的边框 StringBuffer sb = new StringBuffer(); // 用于保存验证码字符串 int index; // 数组的下标 for (int i = 0; i < NUMBER_OF_CHS; i++) { index = r.nextInt(chs.length); // 随机一个下标 g.setColor(new Color(r.nextInt(88), r.nextInt(210), r.nextInt(150))); // 随机一个颜色 g.drawString(chs[index] + "", 15 * i + 3, 18); // 画出字符 sb.append(chs[index]); // 验证码字符串 } /*request.getSession().setAttribute("piccode", sb.toString()); // 将验证码字符串保存到session中 ImageIO.write(image, "jpg", response.getOutputStream());*/ return null; }catch (Exception e){ logger.error("生成验证码失败,",e); verifyCodeImage = new ResponseEntity<>("", HttpStatus.INTERNAL_SERVER_ERROR); } return verifyCodeImage; } /** * 校验验证码 * @param pd 页面请求对象 * @return */ public ResponseEntity<String> validate(IPageData pd){ logger.debug("校验验证码参数:{}",pd.toString()); ResponseEntity<String> verifyResult = null; Assert.jsonObjectHaveKey(pd.getReqData(),"code","请求报文中未包含 code节点"+pd.toString()); String code = CommonCache.getValue(pd.getSessionId()+"_validateCode"); if(JSONObject.parseObject(pd.getReqData()).getString("code").equals(code)){ verifyResult = new ResponseEntity<>("成功", HttpStatus.OK); }else{ verifyResult = new ResponseEntity<>("验证码错误", HttpStatus.INTERNAL_SERVER_ERROR); } return verifyResult; } public RestTemplate getRestTemplate() { return restTemplate; } WebService/src/main/resources/components/login/login.html
@@ -11,6 +11,7 @@ <div class="form-group"> <input type="password" v-model="loginInfo.passwd" class="form-control" placeholder="密码" required=""> </div> <vc:create name="validate-code"></vc:create> <button type="submit" v-on:click="doLogin()" class="btn btn-primary block full-width m-b">登录</button> <a href="#"><small>忘记密码?</small></a> WebService/src/main/resources/components/login/login.js
@@ -29,7 +29,11 @@ } } }); vc.component.$on('errorInfoEvent',function(_errorInfo){ vm.loginInfo.errorInfo = _errorInfo; console.log('errorInfoEvent 事件被监听',_errorInfo) }) })(window.vc); WebService/src/main/resources/components/validate_code/validate-code.html
New file @@ -0,0 +1,4 @@ <div class="form-group" id="validate-code"> <input type="text" v-model="code" class="form-control" placeholder="请输入验证码" required=""/> <img v-bind:src="codeImage" v-on:click="testEvent()"/> </div> WebService/src/main/resources/components/validate_code/validate-code.js
New file @@ -0,0 +1,43 @@ (function(vc){ var vm = new Vue({ el:'#validate-code', data:{ code:'', codeImage:'', errorInfo:'' }, mounted:function(){ this.generateCode(); }, methods:{ generateCode(){ var param = { _uId:'123' } vc.http.call('validate-code','generateValidateCode',param, { emulateJSON:true },function(json,res){ //vm.menus = vm.refreshMenuActive(JSON.parse(json),0); if(res.status == 200){ vm.codeImage = json; return ; } vm.$emit('errorInfoEvent',json); },function(errInfo,error){ console.log('请求失败处理'); vm.loginInfo.errorInfo = errInfo; }) }, testEvent(){ //vc.dispatchEvent('errorInfoEvent',"测试"); vc.component.$emit('errorInfoEvent',"测试"); console.log("testEvent") } } }); })(window.vc); WebService/src/main/resources/static/js/core.js
@@ -7,10 +7,15 @@ vc = { version:"v0.0.1", name:"vue component", author:'java110' author:'java110', component:new Vue() }; var component = window.component || new Vue({ methods:{ } }); //通知window对象 window.vc = vc; })(window); WebService/src/main/resources/views/login.html
@@ -17,9 +17,9 @@ </head> <body class="gray-bg"> <div class="middle-box text-center loginscreen animated fadeInDown"> <vc:create name="login"></vc:create> </div> java110-common/src/main/java/com/java110/common/cache/CommonCache.java
New file @@ -0,0 +1,79 @@ package com.java110.common.cache; import redis.clients.jedis.Jedis; /** * Created by wuxw on 2018/5/5. */ public class CommonCache extends BaseCache{ public final static int defaultExpireTime = 5*60; /** * 获取值(用户ID) * @returne */ public static String getValue(String key){ Jedis redis = null; try { redis = getJedis(); return redis.get(key); }finally { if(redis != null){ redis.close(); } } } /** * 保存数据 * @param key */ public static void setValue(String key,String value,int expireTime){ Jedis redis = null; try { redis = getJedis(); redis.set(key,value); redis.expire(key,expireTime); }finally { if(redis != null){ redis.close(); } } } /** * 删除记录 * @param key */ public static void removeValue(String key){ Jedis redis = null; try { redis = getJedis(); redis.del(key); }finally { if(redis != null){ redis.close(); } } } /** * 重设超时间 * @param jdi * @param expireTime */ public static void resetExpireTime(String jdi,int expireTime){ Jedis redis = null; try { redis = getJedis(); redis.expire(jdi,expireTime); }finally { if(redis != null){ redis.close(); } } } } java110-core/src/main/java/com/java110/core/factory/ValidateCodeFactory.java
New file @@ -0,0 +1,272 @@ package com.java110.core.factory; import sun.misc.BASE64Encoder; import java.awt.Color; import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.io.*; import java.util.Arrays; import java.util.Random; import javax.imageio.ImageIO; /** * <p><b>ValidateCodeFactory Description:</b> (验证码生成)</p> * <b>DATE:</b> 2016年6月2日 下午3:53:34 */ public class ValidateCodeFactory{ //使用到Algerian字体,系统里没有的话需要安装字体,字体只显示大写,去掉了1,0,i,o几个容易混淆的字符 public static final String VERIFY_CODES = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ"; private static Random random = new Random(); static BASE64Encoder encoder = new sun.misc.BASE64Encoder(); /** * 使用系统默认字符源生成验证码 * @param verifySize 验证码长度 * @return */ public static String generateVerifyCode(int verifySize){ return generateVerifyCode(verifySize, VERIFY_CODES); } /** * 使用指定源生成验证码 * @param verifySize 验证码长度 * @param sources 验证码字符源 * @return */ public static String generateVerifyCode(int verifySize, String sources){ if(sources == null || sources.length() == 0){ sources = VERIFY_CODES; } int codesLen = sources.length(); Random rand = new Random(System.currentTimeMillis()); StringBuilder verifyCode = new StringBuilder(verifySize); for(int i = 0; i < verifySize; i++){ verifyCode.append(sources.charAt(rand.nextInt(codesLen-1))); } return verifyCode.toString(); } /** * 生成随机验证码文件,并返回验证码值 * @param w * @param h * @param outputFile * @param verifySize * @return * @throws IOException */ public static String outputVerifyImage(int w, int h, File outputFile, int verifySize) throws IOException{ String verifyCode = generateVerifyCode(verifySize); outputImage(w, h, outputFile, verifyCode); return verifyCode; } /** * 输出随机验证码图片流,并返回验证码值 * @param w * @param h * @param os * @param verifySize * @return * @throws IOException */ public static String outputVerifyImage(int w, int h, OutputStream os, int verifySize) throws IOException{ String verifyCode = generateVerifyCode(verifySize); outputImage(w, h, verifyCode); return verifyCode; } /** * 生成指定验证码图像文件 * @param w * @param h * @param outputFile * @param code * @throws IOException */ public static void outputImage(int w, int h, File outputFile, String code) throws IOException{ if(outputFile == null){ return; } File dir = outputFile.getParentFile(); if(!dir.exists()){ dir.mkdirs(); } try{ outputFile.createNewFile(); FileOutputStream fos = new FileOutputStream(outputFile); outputImage(w, h, code); fos.close(); } catch(IOException e){ throw e; } } /** * 输出指定验证码图片流 * @param w * @param h * @param code * @throws IOException */ public static String outputImage(int w, int h, String code) throws IOException{ int verifySize = code.length(); BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); Random rand = new Random(); Graphics2D g2 = image.createGraphics(); g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON); Color[] colors = new Color[5]; Color[] colorSpaces = new Color[] { Color.WHITE, Color.CYAN, Color.GRAY, Color.LIGHT_GRAY, Color.MAGENTA, Color.ORANGE, Color.PINK, Color.YELLOW }; float[] fractions = new float[colors.length]; for(int i = 0; i < colors.length; i++){ colors[i] = colorSpaces[rand.nextInt(colorSpaces.length)]; fractions[i] = rand.nextFloat(); } Arrays.sort(fractions); g2.setColor(Color.GRAY);// 设置边框色 g2.fillRect(0, 0, w, h); Color c = getRandColor(200, 250); g2.setColor(c);// 设置背景色 g2.fillRect(0, 2, w, h-4); //绘制干扰线 Random random = new Random(); g2.setColor(getRandColor(160, 200));// 设置线条的颜色 for (int i = 0; i < 20; i++) { int x = random.nextInt(w - 1); int y = random.nextInt(h - 1); int xl = random.nextInt(6) + 1; int yl = random.nextInt(12) + 1; g2.drawLine(x, y, x + xl + 40, y + yl + 20); } // 添加噪点 float yawpRate = 0.05f;// 噪声率 int area = (int) (yawpRate * w * h); for (int i = 0; i < area; i++) { int x = random.nextInt(w); int y = random.nextInt(h); int rgb = getRandomIntColor(); image.setRGB(x, y, rgb); } shear(g2, w, h, c);// 使图片扭曲 g2.setColor(getRandColor(100, 160)); int fontSize = h-4; Font font = new Font("Algerian", Font.ITALIC, fontSize); g2.setFont(font); char[] chars = code.toCharArray(); for(int i = 0; i < verifySize; i++){ AffineTransform affine = new AffineTransform(); affine.setToRotation(Math.PI / 4 * rand.nextDouble() * (rand.nextBoolean() ? 1 : -1), (w / verifySize) * i + fontSize/2, h/2); g2.setTransform(affine); g2.drawChars(chars, i, 1, ((w-10) / verifySize) * i + 5, h/2 + fontSize/2 - 10); } g2.dispose(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); ImageIO.write(image, "jpg", baos); byte[] bytes = baos.toByteArray(); return "data:image/jpeg;base64,"+encoder.encodeBuffer(bytes).trim(); } private static Color getRandColor(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 int getRandomIntColor() { int[] rgb = getRandomRgb(); int color = 0; for (int c : rgb) { color = color << 8; color = color | c; } return color; } private static int[] getRandomRgb() { int[] rgb = new int[3]; for (int i = 0; i < 3; i++) { rgb[i] = random.nextInt(255); } return rgb; } private static void shear(Graphics g, int w1, int h1, Color color) { shearX(g, w1, h1, color); shearY(g, w1, h1, color); } private static void shearX(Graphics g, int w1, int h1, Color color) { int period = random.nextInt(2); boolean borderGap = true; int frames = 1; int phase = random.nextInt(2); for (int i = 0; i < h1; i++) { double d = (double) (period >> 1) * Math.sin((double) i / (double) period + (6.2831853071795862D * (double) phase) / (double) frames); g.copyArea(0, i, w1, 1, (int) d, 0); if (borderGap) { g.setColor(color); g.drawLine((int) d, i, 0, i); g.drawLine((int) d + w1, i, w1, i); } } } private static void shearY(Graphics g, int w1, int h1, Color color) { int period = random.nextInt(40) + 10; // 50; boolean borderGap = true; int frames = 20; int phase = 7; for (int i = 0; i < w1; i++) { double d = (double) (period >> 1) * Math.sin((double) i / (double) period + (6.2831853071795862D * (double) phase) / (double) frames); g.copyArea(i, 0, 1, h1, 0, (int) d); if (borderGap) { g.setColor(color); g.drawLine(i, (int) d, i, 0); g.drawLine(i, (int) d + h1, i, h1); } } } public static void main(String[] args) throws IOException{ //File dir = new File("F:/verifies"); int w = 200, h = 80; String verifyCode = generateVerifyCode(4); //File file = new File(dir, verifyCode + ".jpg"); System.out.println(outputImage(w, h, verifyCode)); } }