diff --git a/README.md b/README.md index d20491d..5f930bd 100644 --- a/README.md +++ b/README.md @@ -5,48 +5,54 @@ ## 1.简介 + Java图形验证码,支持gif、中文、算术等类型,可用于Java Web、JavaSE等项目。 -  Java图形验证码,支持gif验证码、中文验证码,可用于Java Web、JavaSE项目。 - +--- ## 2.效果展示 -**gif效果:** - -![验证码](https://ws1.sinaimg.cn/large/006a7GCKly1ftpd30vgc2g303m01c0st.jpg) +![验证码](https://s2.ax1x.com/2019/08/23/msFrE8.png)    -![验证码](https://ws1.sinaimg.cn/large/006a7GCKly1ftpd22ixh9g303m01c3yj.jpg) +![验证码](https://s2.ax1x.com/2019/08/23/msF0DP.png)    -![验证码](https://ws1.sinaimg.cn/large/006a7GCKly1ftpd2k0gjug303m01ct8t.jpg) - -**png效果:** - -![验证码](https://ws1.sinaimg.cn/large/006a7GCKly1ftpd09lqpaj303m01cwee.jpg) +![验证码](https://s2.ax1x.com/2019/08/23/msFwut.png) +
+![验证码](https://s2.ax1x.com/2019/08/23/msFzVK.gif)    -![验证码](https://ws1.sinaimg.cn/large/006a7GCKly1ftpd0tj6nhj303m01ct8m.jpg) +![验证码](https://s2.ax1x.com/2019/08/23/msFvb6.gif)    -![验证码](https://ws1.sinaimg.cn/large/006a7GCKly1ftpd1cw3pcj303m01c0sn.jpg) +![验证码](https://s2.ax1x.com/2019/08/23/msFXK1.gif) +**算术类型:** -**中文验证码:** - -![验证码](https://ws1.sinaimg.cn/large/006a7GCKly1ftovnvbgx6g303m01cjrc.jpg) +![验证码](https://s2.ax1x.com/2019/08/23/mskKPg.png)    -![验证码](https://ws1.sinaimg.cn/large/006a7GCKly1ftovojp2gng303m01cq2w.jpg) +![验证码](https://s2.ax1x.com/2019/08/23/msknIS.png)    -![验证码](https://ws1.sinaimg.cn/large/006a7GCKly1ftovp5u4c4g303m01cdft.jpg) +![验证码](https://s2.ax1x.com/2019/08/23/mskma8.png) -![验证码](https://ws1.sinaimg.cn/large/006a7GCKly1ftovmfxg71j303m01cq2s.jpg) +**中文类型:** + +![验证码](https://s2.ax1x.com/2019/08/23/mskcdK.png)    -![验证码](https://ws1.sinaimg.cn/large/006a7GCKly1ftovmy720dj303m01cq2s.jpg) +![验证码](https://s2.ax1x.com/2019/08/23/msk6Z6.png)    -![验证码](https://ws1.sinaimg.cn/large/006a7GCKly1ftovn68um6j303m01cglf.jpg) +![验证码](https://s2.ax1x.com/2019/08/23/msksqx.png) + +**内置字体:** + +![验证码](https://s2.ax1x.com/2019/08/23/msAVSJ.png) +   +![验证码](https://s2.ax1x.com/2019/08/23/msAAW4.png) +   +![验证码](https://s2.ax1x.com/2019/08/23/msAkYF.png) +--- ## 3.导入项目 -### 2.1.gradle方式的引入 +### 3.1.gradle方式的引入 需要先在project的build.gradle中添加: ```text allprojects { @@ -58,11 +64,11 @@ allprojects { 在项目的build.gradle中添加 ```text dependencies { - compile 'com.github.whvcse:EasyCaptcha:1.5.0' + compile 'com.github.whvcse:EasyCaptcha:1.6.0' } ``` -### 2.2.maven方式引入 +### 3.2.maven方式引入 在你的pom.xml中添加如下代码: ```xml @@ -77,33 +83,49 @@ dependencies { com.github.whvcse EasyCaptcha - 1.5.0 + 1.6.0 - ``` -### 2.3.jar包下载 -[EasyCaptcha-1.5.0.jar](https://gitee.com/whvse/EasyCaptcha/releases) +### 3.3.jar包下载 +[EasyCaptcha-1.6.0.jar](https://gitee.com/whvse/EasyCaptcha/releases) maven导入jar包,在项目根目录创建`libs`文件夹,然后pom.xml添加如下: ``` com.github.whvcse EasyCaptcha - 1.5.0 - ${basedir}/libs/EasyCaptcha-1.5.0.jar + 1.6.0 + ${basedir}/libs/EasyCaptcha-1.6.0.jar ``` --- +## 4.使用方法 -## 3.使用方法 +### 4.1.在SpringMVC中使用 +```java +@Controller +public class CaptchaController { + + @RequestMapping("/captcha") + public void captcha(HttpServletRequest request, HttpServletResponse response) throws Exception { + CaptchaUtil.out(request, response); + } +} +``` +前端html代码: +```html + +``` -### 3.1.快速使用 -1.在web.xml里面加入如下配置: +> 不要忘了把`/captcha`路径排除登录拦截,比如shiro的拦截。 + +### 4.2.在servlet中使用 +web.xml中配置servlet: ```xml @@ -113,163 +135,130 @@ maven导入jar包,在项目根目录创建`libs`文件夹,然后pom.xml添 CaptchaServlet -    /images/captcha +    /captcha ``` -2.前端代码 +前端html代码: ```html - + ``` -### 3.2.在SpringMVC中使用 -也可以使用controller的形式输出验证码,方法如下: -```java -@Controller -public class MainController { - - @RequestMapping("/images/captcha") - public void captcha(HttpServletRequest request, HttpServletResponse response) throws Exception { - CaptchaUtil.out(request, response); - } -} -``` -前端代码: -```html - -``` - -### 3.3.判断验证码是否正确 +### 4.3.判断验证码是否正确 ```java @Controller public class LoginController { @PostMapping("/login") - public JsonResult login(String username,String password,String code){ - - if (!CaptchaUtil.ver(code, request)) { - CaptchaUtil.clear(request); + public JsonResult login(String username,String password,String verCode){ + if (!CaptchaUtil.ver(verCode, request)) { + CaptchaUtil.clear(request); // 清除session中的验证码 return JsonResult.error("验证码不正确"); } } } ``` -### 3.4.设置宽高和位数 +### 4.4.设置宽高和位数 ```java @Controller -public class MainController { +public class CaptchaController { - @RequestMapping("/images/captcha") + @RequestMapping("/captcha") public void captcha(HttpServletRequest request, HttpServletResponse response) throws Exception { // 设置位数 CaptchaUtil.out(5, request, response); - // 设置宽、高、位数 CaptchaUtil.out(130, 48, 5, request, response); + + // 使用gif验证码 + GifCaptcha gifCaptcha = new GifCaptcha(130,48,4); + CaptchaUtil.out(gifCaptcha, request, response); } } ``` -### 3.5.不使用工具类 - -  CaptchaUtil是为了简化操作,封装了生成验证码、存session、判断验证码等功能。CaptchaUtil使用的GifCaptcha -生成的字母数字混合的gif验证码,如果需要设置更多的参数,请参照如下操作使用: +### 4.5.不使用工具类 + CaptchaUtil封装了输出验证码、存session、判断验证码等功能,也可以不使用此工具类: ```java @Controller -public class MainController { +public class CaptchaController { - @RequestMapping("/images/captcha") + @RequestMapping("/captcha") public void captcha(HttpServletRequest request, HttpServletResponse response) throws Exception { // 设置请求头为输出图片类型 - CaptchaUtil.setHeader(response); + response.setContentType("image/gif"); + response.setHeader("Pragma", "No-cache"); + response.setHeader("Cache-Control", "no-cache"); + response.setDateHeader("Expires", 0); // 三个参数分别为宽、高、位数 - GifCaptcha gifCaptcha = new GifCaptcha(130, 48, 5); - + SpecCaptcha specCaptcha = new SpecCaptcha(130, 48, 5); // 设置字体 - gifCaptcha.setFont(new Font("Verdana", Font.PLAIN, 32)); // 有默认字体,可以不用设置 - + specCaptcha.setFont(new Font("Verdana", Font.PLAIN, 32)); // 有默认字体,可以不用设置 // 设置类型,纯数字、纯字母、字母数字混合 - gifCaptcha.setCharType(Captcha.TYPE_ONLY_NUMBER); + specCaptcha.setCharType(Captcha.TYPE_ONLY_NUMBER); // 验证码存入session - request.getSession().setAttribute("captcha", gifCaptcha.text().toLowerCase()); + request.getSession().setAttribute("captcha", specCaptcha.text().toLowerCase()); // 输出图片流 - gifCaptcha.out(response.getOutputStream()); + specCaptcha.out(response.getOutputStream()); } @PostMapping("/login") - public JsonResult login(String username,String password,String code){ + public JsonResult login(String username,String password,String verCode){ // 获取session中的验证码 String sessionCode = request.getSession().getAttribute("captcha"); // 判断验证码 - if (code==null || !sessionCode.equals(code.trim().toLowerCase())) { + if (verCode==null || !sessionCode.equals(verCode.trim().toLowerCase())) { return JsonResult.error("验证码不正确"); } } } ``` -## 4.更多设置 +## 5.更多设置 -### 4.1.使用Gif验证码 +### 5.1.验证码类型 ```java public class Test { public static void main(String[] args) { - OutputStream outputStream = new FileOutputStream(new File("D:/a/aa.gif")); + // png类型 + SpecCaptcha captcha = new SpecCaptcha(130, 48, 5); + captcha.text(); // 获取验证码的字符 + captcha.textChar(); // 获取验证码的字符数组 - // 三个参数分别为宽、高、位数 - GifCaptcha gifCaptcha = new GifCaptcha(130, 48, 5); + // gif类型 + GifCaptcha captcha = new GifCaptcha(130, 48, 5); - // 设置字体 - gifCaptcha.setFont(new Font("Verdana", Font.PLAIN, 32)); // 有默认字体,可以不用设置 + // 中文类型 + ChineseCaptcha captcha = new ChineseCaptcha(130, 48, 5); - // 设置类型,纯数字、纯字母、字母数字混合 - gifCaptcha.setCharType(Captcha.TYPE_ONLY_NUMBER); + // 中文gif类型 + ChineseGifCaptcha captcha = new ChineseGifCaptcha(130, 48, 5); - // 生成的验证码 - String code = gifCaptcha.text(); + // 算术类型 + ArithmeticCaptcha captcha = new ArithmeticCaptcha(130, 48, 5); + captcha.setLen(3); // 几位数运算,默认是两位 + captcha.getArithmeticString(); // 获取运算的公式:3+2=? + captcha.text(); // 获取运算的结果:5 - // 输出图片流 - gifCaptcha.out(outputStream); + captcha.out(outputStream); // 输出验证码 } } ``` -### 4.2.使用png验证码 +> 注意:
+>  算术验证码的len表示是几位数运算,而其他验证码的len表示验证码的位数,算术验证码的text()表示的是公式的结果, +> 对于算术验证码,你应该把公式的结果存储session,而不是公式。 -```java -public class Test { - - public static void main(String[] args) { - OutputStream outputStream = new FileOutputStream(new File("D:/a/aa.png")); - - // 三个参数分别为宽、高、位数 - SpecCaptcha specCaptcha = new SpecCaptcha(130, 48, 5); - - // 设置字体 - specCaptcha.setFont(new Font("Verdana", Font.PLAIN, 32)); // 有默认字体,可以不用设置 - - // 设置类型,纯数字、纯字母、字母数字混合 - specCaptcha.setCharType(Captcha.TYPE_ONLY_NUMBER); - - // 生成的验证码 - String code = specCaptcha.text(); - - // 输出图片流 - specCaptcha.out(outputStream); - } -} -``` - -### 4.3.验证码类型 +### 5.2.验证码字符类型 类型 | 描述 :--- | :--- @@ -280,60 +269,145 @@ public class Test { TYPE_ONLY_LOWER | 纯小写字母 TYPE_NUM_AND_UPPER | 数字和大写字母 - -### 4.4.中文验证码 - -中文png验证码: - -```java -public class Test { - - public static void main(String[] args) { - OutputStream outputStream = new FileOutputStream(new File("D:/a/aa.png")); - - // 三个参数分别为宽、高、位数 - ChineseCaptcha chineseCaptcha = new ChineseCaptcha(130, 48, 4); - - // 设置字体 - chineseCaptcha.setFont(new Font("楷体", Font.PLAIN, 28)); // 有默认字体,可以不用设置 - - // 生成的验证码 - String code = chineseCaptcha.text(); - - // 输出图片流 - chineseCaptcha.out(outputStream); - } -} +使用方法: +``` +SpecCaptcha captcha = new SpecCaptcha(130, 48, 5); +captcha.setCharType(Captcha.TYPE_ONLY_NUMBER); ``` -中文gif验证码: +> 只有`SpecCaptcha`和`GifCaptcha`设置才有效果。 -```java -public class Test { - - public static void main(String[] args) { - OutputStream outputStream = new FileOutputStream(new File("D:/a/aa.png")); - - // 三个参数分别为宽、高、位数 - ChineseGifCaptcha chineseGifCaptcha = new ChineseGifCaptcha(130, 48, 4); - - // 设置字体 - chineseGifCaptcha.setFont(new Font("楷体", Font.PLAIN, 28)); // 有默认字体,可以不用设置 +### 5.3.字体设置 +内置字体: - // 生成的验证码 - String code = chineseGifCaptcha.text(); - - // 输出图片流 - chineseGifCaptcha.out(outputStream); - } -} + 字体 | 效果 + :--- | :--- + Captcha.FONT_1 | ![](https://s2.ax1x.com/2019/08/23/msMe6U.png) + Captcha.FONT_2 | ![](https://s2.ax1x.com/2019/08/23/msMAf0.png) + Captcha.FONT_3 | ![](https://s2.ax1x.com/2019/08/23/msMCwj.png) + Captcha.FONT_4 | ![](https://s2.ax1x.com/2019/08/23/msM9mQ.png) + Captcha.FONT_5 | ![](https://s2.ax1x.com/2019/08/23/msKz6S.png) + Captcha.FONT_6 | ![](https://s2.ax1x.com/2019/08/23/msKxl8.png) + Captcha.FONT_7 | ![](https://s2.ax1x.com/2019/08/23/msMPTs.png) + Captcha.FONT_8 | ![](https://s2.ax1x.com/2019/08/23/msMmXF.png) + Captcha.FONT_9 | ![](https://s2.ax1x.com/2019/08/23/msMVpV.png) + Captcha.FONT_10 | ![](https://s2.ax1x.com/2019/08/23/msMZlT.png) + +使用方法: +``` +SpecCaptcha captcha = new SpecCaptcha(130, 48, 5); + +// 设置内置字体 +captcha.setFont(Captcha.FONT_1); + +// 设置系统字体 +captcha.setFont(new Font("楷体", Font.PLAIN, 28)); ``` -### 4.5.前后端分离项目的使用 +### 5.4.输出base64编码 +``` +SpecCaptcha specCaptcha = new SpecCaptcha(130, 48, 5); +specCaptcha.toBase64(); -  分离项目建议不要存储在session中,存储在redis中。 +// 如果不想要base64的头部data:image/png;base64, +specCaptcha.toBase64(""); // 加一个空的参数即可 +``` +### 5.5.输出到文件 +``` +FileOutputStream outputStream = new FileOutputStream(new File("C:/captcha.png")) +SpecCaptcha specCaptcha = new SpecCaptcha(130, 48, 5); +specCaptcha.out(outputStream); +``` -## 5.自定义效果 +--- -参考源代码中的SpecCaptcha和GifCaptcha继承Captcha即可。 +## 6.前后端分离项目的使用 + + 前后端分离项目建议不要存储在session中,存储在redis中,redis存储需要一个key,key一同返回给前端用于验证输入: +```java +@Controller +public class CaptchaController { + @Autowired + private RedisUtil redisUtil; + + @ResponseBody + @RequestMapping("/captcha") + public JsonResult captcha(HttpServletRequest request, HttpServletResponse response) throws Exception { + SpecCaptcha specCaptcha = new SpecCaptcha(130, 48, 5); + String verCode = specCaptcha.text().toLowerCase(); + String key = UUID.randomUUID().toString(); + // 存入redis并设置过期时间为30分钟 + redisUtil.setEx(key, verCode, 30, TimeUnit.MINUTES); + // 将key和base64返回给前端 + return JsonResult.ok().put("key", key).put("image", specCaptcha.toBase64()); + } + + @ResponseBody + @PostMapping("/login") + public JsonResult login(String username,String password,String verCode,String verKey){ + // 获取redis中的验证码 + String redisCode = redisUtil.get(verKey); + // 判断验证码 + if (verCode==null || !redisCode.equals(verCode.trim().toLowerCase())) { + return JsonResult.error("验证码不正确"); + } + } +} +``` +前端使用ajax获取验证码: +```html + + + +``` + +> RedisUtil到这里获取[https://gitee.com/whvse/RedisUtil](https://gitee.com/whvse/RedisUtil) + +--- + +## 7.自定义效果 + + 继承`Captcha`实现`out`方法,中文验证码可继承`ChineseCaptchaAbstract`,算术验证码可继承`ArithmeticCaptchaAbstract`。 + +--- + +## 8.更新日志 + +- **2019-08-23 (v1.6.0)** + - 增加10种漂亮的内置字体,不依赖系统字体 + + - 增加算术验证码,运算位数可自由配置 + - 增加输出base64编码的功能 + - 增加贝塞尔曲线作为干扰线 + +- **2018-08-09 (v1.5.0)** + - 增加纯大写字母、纯小写字母、数字和大写字母配置 + + - 增加中文验证码、中文gif验证码 + - 增加抗锯齿效果,优化文字颜色 + - 增加CaptchaUtil便于Web项目使用 + +--- + +## 9.推荐 + **EasyWeb管系统模板**,一个开箱即用的后台模板,使用简单,功能丰富,包含ifram版和spa单页面版,[前往查看](https://easyweb.vip)。 + +![](https://s2.ax1x.com/2019/08/23/msiO78.png) diff --git a/pom.xml b/pom.xml index 5070b53..7352603 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.wf easy-captcha - 1.5.0-RELEASE + 1.6.0-RELEASE jar EasyCaptcha @@ -23,7 +23,7 @@ junit junit - 4.7 + 4.12 test diff --git a/src/main/java/com/wf/captcha/ArithmeticCaptcha.java b/src/main/java/com/wf/captcha/ArithmeticCaptcha.java new file mode 100644 index 0000000..b29f851 --- /dev/null +++ b/src/main/java/com/wf/captcha/ArithmeticCaptcha.java @@ -0,0 +1,96 @@ +package com.wf.captcha; + +import com.wf.captcha.base.ArithmeticCaptchaAbstract; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.io.OutputStream; + +/** + * png格式验证码 + * Created by 王帆 on 2018-07-27 上午 10:08. + */ +public class ArithmeticCaptcha extends ArithmeticCaptchaAbstract { + + public ArithmeticCaptcha() { + } + + public ArithmeticCaptcha(int width, int height) { + this(); + setWidth(width); + setHeight(height); + } + + public ArithmeticCaptcha(int width, int height, int len) { + this(width, height); + setLen(len); + } + + public ArithmeticCaptcha(int width, int height, int len, Font font) { + this(width, height, len); + setFont(font); + } + + /** + * 生成验证码 + * + * @param out 输出流 + * @return 是否成功 + */ + @Override + public boolean out(OutputStream out) { + checkAlpha(); + return graphicsImage(getArithmeticString().toCharArray(), out); + } + + @Override + public String toBase64() { + return toBase64("data:image/png;base64,"); + } + + /** + * 生成验证码图形 + * + * @param strs 验证码 + * @param out 输出流 + * @return boolean + */ + private boolean graphicsImage(char[] strs, OutputStream out) { + try { + BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + Graphics2D g2d = (Graphics2D) bi.getGraphics(); + // 填充背景 + g2d.setColor(Color.WHITE); + g2d.fillRect(0, 0, width, height); + // 抗锯齿 + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + // 画干扰圆 + drawOval(2, g2d); + // 画字符串 + g2d.setFont(getFont()); + FontMetrics fontMetrics = g2d.getFontMetrics(); + int fW = width / strs.length; // 每一个字符所占的宽度 + int fSp = (fW - (int) fontMetrics.getStringBounds("8", g2d).getWidth()) / 2; // 字符的左右边距 + for (int i = 0; i < strs.length; i++) { + g2d.setColor(color()); + int fY = height - ((height - (int) fontMetrics.getStringBounds(String.valueOf(strs[i]), g2d).getHeight()) >> 1); // 文字的纵坐标 + g2d.drawString(String.valueOf(strs[i]), i * fW + fSp + 3, fY - 3); + } + g2d.dispose(); + ImageIO.write(bi, "png", out); + out.flush(); + return true; + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + out.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + return false; + } +} \ No newline at end of file diff --git a/src/main/java/com/wf/captcha/ChineseCaptcha.java b/src/main/java/com/wf/captcha/ChineseCaptcha.java index 2c3af93..a05cc83 100644 --- a/src/main/java/com/wf/captcha/ChineseCaptcha.java +++ b/src/main/java/com/wf/captcha/ChineseCaptcha.java @@ -38,10 +38,14 @@ public class ChineseCaptcha extends ChineseCaptchaAbstract { */ @Override public boolean out(OutputStream out) { - checkAlpha(); return graphicsImage(textChar(), out); } + @Override + public String toBase64() { + return toBase64("data:image/png;base64,"); + } + /** * 生成验证码图形 * @@ -50,54 +54,34 @@ public class ChineseCaptcha extends ChineseCaptchaAbstract { * @return boolean */ private boolean graphicsImage(char[] strs, OutputStream out) { - boolean ok; try { BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); - Graphics2D g = (Graphics2D) bi.getGraphics(); - AlphaComposite ac3; - int len = strs.length; - g.setColor(Color.WHITE); - g.fillRect(0, 0, width, height); + Graphics2D g2d = (Graphics2D) bi.getGraphics(); + // 填充背景 + g2d.setColor(Color.WHITE); + g2d.fillRect(0, 0, width, height); // 抗锯齿 - g.setColor(color()); - Font font = getFont(); - g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - int hp = (height - font.getSize()) >> 1; - int h = height - hp; - int w = width / strs.length; - int sp = (w - font.getSize()) / 2; + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + // 画干扰圆 + drawOval(3, g2d); + // 画干扰线 + g2d.setStroke(new BasicStroke(1.2f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); + drawBesselLine(1, g2d); // 画字符串 - for (int i = 0; i < len; i++) { - // 计算坐标 - int x = i * w + sp + num(-5, 5); - int y = h + num(-5, 5); - if (x < 5) { - x = 5; - } - if (x + font.getSize() > width) { - x = width - font.getSize(); - } - if (y > height) { - y = height; - } - if (y - font.getSize() < 0) { - y = font.getSize(); - } - g.setFont(font.deriveFont(num(2) == 0 ? Font.PLAIN : Font.ITALIC)); - g.drawString(String.valueOf(strs[i]), x, y); + g2d.setFont(getFont()); + FontMetrics fontMetrics = g2d.getFontMetrics(); + int fW = width / strs.length; // 每一个字符所占的宽度 + int fSp = (fW - (int) fontMetrics.getStringBounds("王", g2d).getWidth()) / 2; // 字符的左右边距 + for (int i = 0; i < strs.length; i++) { + g2d.setColor(color()); + int fY = height - ((height - (int) fontMetrics.getStringBounds(String.valueOf(strs[i]), g2d).getHeight()) >> 1); // 文字的纵坐标 + g2d.drawString(String.valueOf(strs[i]), i * fW + fSp + 3, fY - 3); } - // 随机画干扰线 - g.setStroke(new BasicStroke(1.25f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); - ac3 = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.7f); // 指定透明度 - g.setComposite(ac3); - drawLine(2, g.getColor(), g); - // 画干扰圆圈 - drawOval(5, g.getColor(), g); + g2d.dispose(); ImageIO.write(bi, "png", out); out.flush(); - ok = true; + return true; } catch (IOException e) { - ok = false; e.printStackTrace(); } finally { try { @@ -106,6 +90,6 @@ public class ChineseCaptcha extends ChineseCaptchaAbstract { e.printStackTrace(); } } - return ok; + return false; } } diff --git a/src/main/java/com/wf/captcha/ChineseGifCaptcha.java b/src/main/java/com/wf/captcha/ChineseGifCaptcha.java index 4cea9fd..7f2e35e 100644 --- a/src/main/java/com/wf/captcha/ChineseGifCaptcha.java +++ b/src/main/java/com/wf/captcha/ChineseGifCaptcha.java @@ -4,6 +4,7 @@ import com.wf.captcha.base.ChineseCaptchaAbstract; import com.wf.captcha.utils.GifEncoder; import java.awt.*; +import java.awt.geom.CubicCurve2D; import java.awt.image.BufferedImage; import java.io.IOException; import java.io.OutputStream; @@ -30,24 +31,39 @@ public class ChineseGifCaptcha extends ChineseCaptchaAbstract { @Override public boolean out(OutputStream os) { - checkAlpha(); - boolean ok; try { - char[] rands = textChar(); // 获取验证码数组 + char[] strs = textChar(); // 获取验证码数组 + // 随机生成每个文字的颜色 + Color fontColor[] = new Color[len]; + for (int i = 0; i < len; i++) { + fontColor[i] = color(); + } + // 随机生成贝塞尔曲线参数 + int x1 = 5, y1 = num(5, height / 2); + int x2 = width - 5, y2 = num(height / 2, height - 5); + int ctrlx = num(width / 4, width / 4 * 3), ctrly = num(5, height - 5); + if (num(2) == 0) { + int ty = y1; + y1 = y2; + y2 = ty; + } + int ctrlx1 = num(width / 4, width / 4 * 3), ctrly1 = num(5, height - 5); + int[][] besselXY = new int[][]{{x1, y1}, {ctrlx, ctrly}, {ctrlx1, ctrly1}, {x2, y2}}; + // 开始画gif每一帧 GifEncoder gifEncoder = new GifEncoder(); - gifEncoder.start(os); gifEncoder.setQuality(180); gifEncoder.setDelay(100); gifEncoder.setRepeat(0); - BufferedImage frame; - Color fontcolor = color(); + gifEncoder.start(os); for (int i = 0; i < len; i++) { - frame = graphicsImage(fontcolor, rands, i); + BufferedImage frame = graphicsImage(fontColor, strs, i, besselXY); gifEncoder.addFrame(frame); frame.flush(); } gifEncoder.finish(); - ok = true; + return true; + } catch (Exception e) { + e.printStackTrace(); } finally { try { os.close(); @@ -55,60 +71,53 @@ public class ChineseGifCaptcha extends ChineseCaptchaAbstract { e.printStackTrace(); } } - return ok; + return false; + } + + @Override + public String toBase64() { + return toBase64("data:image/gif;base64,"); } /** * 画随机码图 * - * @param fontcolor 随机字体颜色 + * @param fontColor 随机字体颜色 * @param strs 字符数组 - * @param flag 透明度使用 + * @param flag 透明度 + * @param besselXY 干扰线参数 * @return BufferedImage */ - private BufferedImage graphicsImage(Color fontcolor, char[] strs, int flag) { + private BufferedImage graphicsImage(Color[] fontColor, char[] strs, int flag, int[][] besselXY) { BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); Graphics2D g2d = (Graphics2D) image.getGraphics(); - g2d.setColor(Color.WHITE); // 填充背景颜色 + // 填充背景颜色 + g2d.setColor(Color.WHITE); g2d.fillRect(0, 0, width, height); // 抗锯齿 - AlphaComposite ac3; - g2d.setColor(fontcolor); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - // 画验证码 - Font font = getFont(); - int hp = (height - font.getSize()) >> 1; - int h = height - hp; - int w = width / strs.length; - int sp = (w - font.getSize()) / 2; - for (int i = 0; i < len; i++) { - ac3 = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, getAlpha(flag, i)); - g2d.setComposite(ac3); - // 计算坐标 - int x = i * w + sp + num(-3, 3); - int y = h + num(-3, 3); - if (x < 0) { - x = 0; - } - if (x + font.getSize() > width) { - x = width - font.getSize(); - } - if (y > height) { - y = height; - } - if (y - font.getSize() < 0) { - y = font.getSize(); - } - g2d.setFont(font.deriveFont(num(2) == 0 ? Font.PLAIN : Font.ITALIC)); - g2d.drawString(String.valueOf(strs[i]), x, y); - } - // 随机画干扰线 - g2d.setStroke(new BasicStroke(1.25f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); - ac3 = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.45f); - g2d.setComposite(ac3); - drawLine(1, g2d.getColor(), g2d); // 画干扰圆圈 - drawOval(3, g2d.getColor(), g2d); + g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.1f * num(10))); // 设置透明度 + drawOval(2, g2d); + // 画干扰线 + g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.7f)); // 设置透明度 + g2d.setStroke(new BasicStroke(1.2f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); + g2d.setColor(fontColor[0]); + CubicCurve2D shape = new CubicCurve2D.Double(besselXY[0][0], besselXY[0][1], besselXY[1][0], besselXY[1][1], besselXY[2][0], besselXY[2][1], besselXY[3][0], besselXY[3][1]); + g2d.draw(shape); + // 画验证码 + g2d.setFont(getFont()); + FontMetrics fontMetrics = g2d.getFontMetrics(); + int fW = width / strs.length; // 每一个字符所占的宽度 + int fSp = (fW - (int) fontMetrics.getStringBounds("W", g2d).getWidth()) / 2; // 字符的左右边距 + for (int i = 0; i < strs.length; i++) { + // 设置透明度 + AlphaComposite ac3 = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, getAlpha(flag, i)); + g2d.setComposite(ac3); + g2d.setColor(fontColor[i]); + int fY = height - ((height - (int) fontMetrics.getStringBounds(String.valueOf(strs[i]), g2d).getHeight()) >> 1); // 文字的纵坐标 + g2d.drawString(String.valueOf(strs[i]), i * fW + fSp - 3, fY - 3); + } g2d.dispose(); return image; } diff --git a/src/main/java/com/wf/captcha/GifCaptcha.java b/src/main/java/com/wf/captcha/GifCaptcha.java index 6d80c7c..5e59775 100644 --- a/src/main/java/com/wf/captcha/GifCaptcha.java +++ b/src/main/java/com/wf/captcha/GifCaptcha.java @@ -4,6 +4,9 @@ import com.wf.captcha.base.Captcha; import com.wf.captcha.utils.GifEncoder; import java.awt.*; +import java.awt.geom.CubicCurve2D; +import java.awt.geom.QuadCurve2D; +import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.io.IOException; import java.io.OutputStream; @@ -34,27 +37,39 @@ public class GifCaptcha extends Captcha { @Override public boolean out(OutputStream os) { - checkAlpha(); - boolean ok; try { - char[] rands = textChar(); // 获取验证码数组 + char[] strs = textChar(); // 获取验证码数组 + // 随机生成每个文字的颜色 + Color fontColor[] = new Color[len]; + for (int i = 0; i < len; i++) { + fontColor[i] = color(); + } + // 随机生成贝塞尔曲线参数 + int x1 = 5, y1 = num(5, height / 2); + int x2 = width - 5, y2 = num(height / 2, height - 5); + int ctrlx = num(width / 4, width / 4 * 3), ctrly = num(5, height - 5); + if (num(2) == 0) { + int ty = y1; + y1 = y2; + y2 = ty; + } + int ctrlx1 = num(width / 4, width / 4 * 3), ctrly1 = num(5, height - 5); + int[][] besselXY = new int[][]{{x1, y1}, {ctrlx, ctrly}, {ctrlx1, ctrly1}, {x2, y2}}; + // 开始画gif每一帧 GifEncoder gifEncoder = new GifEncoder(); - gifEncoder.start(os); gifEncoder.setQuality(180); gifEncoder.setDelay(100); gifEncoder.setRepeat(0); - BufferedImage frame; - Color fontcolor[] = new Color[len]; + gifEncoder.start(os); for (int i = 0; i < len; i++) { - fontcolor[i] = new Color(20 + num(110), 20 + num(110), 20 + num(110)); - } - for (int i = 0; i < len; i++) { - frame = graphicsImage(fontcolor, rands, i); + BufferedImage frame = graphicsImage(fontColor, strs, i, besselXY); gifEncoder.addFrame(frame); frame.flush(); } gifEncoder.finish(); - ok = true; + return true; + } catch (Exception e) { + e.printStackTrace(); } finally { try { os.close(); @@ -62,18 +77,24 @@ public class GifCaptcha extends Captcha { e.printStackTrace(); } } - return ok; + return false; + } + + @Override + public String toBase64() { + return toBase64("data:image/gif;base64,"); } /** * 画随机码图 * - * @param fontcolor 随机字体颜色 + * @param fontColor 随机字体颜色 * @param strs 字符数组 - * @param flag 透明度使用 + * @param flag 透明度 + * @param besselXY 干扰线参数 * @return BufferedImage */ - private BufferedImage graphicsImage(Color[] fontcolor, char[] strs, int flag) { + private BufferedImage graphicsImage(Color[] fontColor, char[] strs, int flag, int[][] besselXY) { BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); Graphics2D g2d = (Graphics2D) image.getGraphics(); // 填充背景颜色 @@ -81,39 +102,27 @@ public class GifCaptcha extends Captcha { g2d.fillRect(0, 0, width, height); // 抗锯齿 g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - g2d.setStroke(new BasicStroke(1.3f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); // 画干扰圆圈 - drawOval(4, g2d); - // 随机画干扰线 - drawLine(2, g2d); + g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.1f * num(10))); // 设置透明度 + drawOval(2, g2d); + // 画干扰线 + g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.7f)); // 设置透明度 + g2d.setStroke(new BasicStroke(1.2f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); + g2d.setColor(fontColor[0]); + CubicCurve2D shape = new CubicCurve2D.Double(besselXY[0][0], besselXY[0][1], besselXY[1][0], besselXY[1][1], besselXY[2][0], besselXY[2][1], besselXY[3][0], besselXY[3][1]); + g2d.draw(shape); // 画验证码 - Font font = getFont(); - int hp = (height - font.getSize()) >> 1; - int h = height - hp; - int w = width / strs.length; - int sp = (w - font.getSize()) / 2; + g2d.setFont(getFont()); + FontMetrics fontMetrics = g2d.getFontMetrics(); + int fW = width / strs.length; // 每一个字符所占的宽度 + int fSp = (fW - (int) fontMetrics.getStringBounds("W", g2d).getWidth()) / 2; // 字符的左右边距 for (int i = 0; i < strs.length; i++) { + // 设置透明度 AlphaComposite ac3 = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, getAlpha(flag, i)); g2d.setComposite(ac3); - g2d.setColor(fontcolor[i]); - // 计算坐标 - int x = i * w + sp + num(3); - int y = h - num(3, 6); - // 调整溢出的字 - if (x < 8) { - x = 8; - } - if (x + font.getSize() > width) { - x = width - font.getSize(); - } - if (y > height) { - y = height; - } - if (y - font.getSize() < 0) { - y = font.getSize(); - } - g2d.setFont(font.deriveFont(num(2) == 0 ? Font.PLAIN : Font.ITALIC)); - g2d.drawString(String.valueOf(strs[i]), x, y); + g2d.setColor(fontColor[i]); + int fY = height - ((height - (int) fontMetrics.getStringBounds(String.valueOf(strs[i]), g2d).getHeight()) >> 1); // 文字的纵坐标 + g2d.drawString(String.valueOf(strs[i]), i * fW + fSp + 3, fY - 3); } g2d.dispose(); return image; diff --git a/src/main/java/com/wf/captcha/SpecCaptcha.java b/src/main/java/com/wf/captcha/SpecCaptcha.java index 74abf82..2de05b9 100644 --- a/src/main/java/com/wf/captcha/SpecCaptcha.java +++ b/src/main/java/com/wf/captcha/SpecCaptcha.java @@ -45,6 +45,11 @@ public class SpecCaptcha extends Captcha { return graphicsImage(textChar(), out); } + @Override + public String toBase64() { + return toBase64("data:image/png;base64,"); + } + /** * 生成验证码图形 * @@ -55,45 +60,28 @@ public class SpecCaptcha extends Captcha { private boolean graphicsImage(char[] strs, OutputStream out) { try { BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); - Graphics2D g = (Graphics2D) bi.getGraphics(); + Graphics2D g2d = (Graphics2D) bi.getGraphics(); // 填充背景 - g.setColor(Color.WHITE); - g.fillRect(0, 0, width, height); + g2d.setColor(Color.WHITE); + g2d.fillRect(0, 0, width, height); // 抗锯齿 - g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - g.setStroke(new BasicStroke(1.3f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); - // 随机画干扰线 - // drawLine(3, g); - // 随机画干扰圆 - // drawOval(8, g); - drawBesselLine(2, g); + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + // 画干扰圆 + drawOval(2, g2d); + // 画干扰线 + g2d.setStroke(new BasicStroke(2f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); + drawBesselLine(1, g2d); // 画字符串 - // AlphaComposite ac3 = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.8f);// 指定透明度 - // g.setComposite(ac3); - g.setFont(getFont()); - int fontSize = getFont().getSize(); - int fY = height - ((height - fontSize) >> 1); // 文字的纵坐标 + g2d.setFont(getFont()); + FontMetrics fontMetrics = g2d.getFontMetrics(); int fW = width / strs.length; // 每一个字符所占的宽度 - int fSp = (fW - fontSize) / 2; // 字符的左右边距 + int fSp = (fW - (int) fontMetrics.getStringBounds("W", g2d).getWidth()) / 2; // 字符的左右边距 for (int i = 0; i < strs.length; i++) { - g.setColor(color()); - // 计算坐标 - int x = i * fW + fSp; - int y = fY/* - num(3, 6)*/; - /*if (x < 8) { - x = 8; - } - if (x + fontSize > width) { - x = width - fontSize; - }*/ - /*if (y > height) { - y = height; - } - if (y - fontSize < 0) { - y = fontSize; - }*/ - g.drawString(String.valueOf(strs[i]), x, y); + g2d.setColor(color()); + int fY = height - ((height - (int) fontMetrics.getStringBounds(String.valueOf(strs[i]), g2d).getHeight()) >> 1); // 文字的纵坐标 + g2d.drawString(String.valueOf(strs[i]), i * fW + fSp + 3, fY - 3); } + g2d.dispose(); ImageIO.write(bi, "png", out); out.flush(); return true; diff --git a/src/main/java/com/wf/captcha/base/ArithmeticCaptchaAbstract.java b/src/main/java/com/wf/captcha/base/ArithmeticCaptchaAbstract.java new file mode 100644 index 0000000..1d13f76 --- /dev/null +++ b/src/main/java/com/wf/captcha/base/ArithmeticCaptchaAbstract.java @@ -0,0 +1,55 @@ +package com.wf.captcha.base; + +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import javax.script.ScriptException; + +public abstract class ArithmeticCaptchaAbstract extends Captcha { + private String arithmeticString; // 计算公式 + + public ArithmeticCaptchaAbstract() { + setLen(2); + } + + /** + * 生成随机验证码 + * + * @return 验证码字符数组 + */ + @Override + protected char[] alphas() { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < len; i++) { + sb.append(num(10)); + if (i < len - 1) { + int type = num(1, 4); + if (type == 1) { + sb.append("+"); + } else if (type == 2) { + sb.append("-"); + } else if (type == 3) { + sb.append("x"); + } + } + } + ScriptEngineManager manager = new ScriptEngineManager(); + ScriptEngine engine = manager.getEngineByName("javascript"); + try { + chars = String.valueOf(engine.eval(sb.toString().replaceAll("x", "*"))); + } catch (ScriptException e) { + e.printStackTrace(); + } + sb.append("=?"); + arithmeticString = sb.toString(); + return chars.toCharArray(); + } + + public String getArithmeticString() { + checkAlpha(); + return arithmeticString; + } + + public void setArithmeticString(String arithmeticString) { + this.arithmeticString = arithmeticString; + } +} diff --git a/src/main/java/com/wf/captcha/base/Captcha.java b/src/main/java/com/wf/captcha/base/Captcha.java index c70b009..0d56679 100644 --- a/src/main/java/com/wf/captcha/base/Captcha.java +++ b/src/main/java/com/wf/captcha/base/Captcha.java @@ -1,8 +1,11 @@ package com.wf.captcha.base; +import sun.misc.BASE64Encoder; + import java.awt.*; import java.awt.geom.CubicCurve2D; import java.awt.geom.QuadCurve2D; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.OutputStream; @@ -108,6 +111,20 @@ public abstract class Captcha extends Randoms { */ public abstract boolean out(OutputStream os); + public abstract String toBase64(); + + /** + * 输出base64编码 + * + * @param type 编码头 + * @return + */ + public String toBase64(String type) { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + out(outputStream); + return type + new BASE64Encoder().encode(outputStream.toByteArray()); + } + /** * 获取当前的验证码 * @@ -156,7 +173,7 @@ public abstract class Captcha extends Randoms { */ public void drawLine(int num, Color color, Graphics2D g) { for (int i = 0; i < num; i++) { - g.setColor(color == null ? color(150, 250) : color); + g.setColor(color == null ? color() : color); int x1 = num(-10, width - 10); int y1 = num(5, height - 5); int x2 = num(10, width + 10); @@ -172,10 +189,7 @@ public abstract class Captcha extends Randoms { * @param g Graphics2D */ public void drawOval(int num, Graphics2D g) { - for (int i = 0; i < num; i++) { - g.setColor(color(100, 250)); - g.drawOval(num(width), num(height), 10 + num(20), 10 + num(20)); - } + drawOval(num, null, g); } /** @@ -187,8 +201,9 @@ public abstract class Captcha extends Randoms { */ public void drawOval(int num, Color color, Graphics2D g) { for (int i = 0; i < num; i++) { - g.setColor(color == null ? color(100, 250) : color); - g.drawOval(num(width), num(height), 10 + num(20), 10 + num(20)); + g.setColor(color == null ? color() : color); + int w = 5 + num(10); + g.drawOval(num(width - 25), num(height - 15), w, w); } } @@ -212,19 +227,20 @@ public abstract class Captcha extends Randoms { public void drawBesselLine(int num, Color color, Graphics2D g) { for (int i = 0; i < num; i++) { g.setColor(color == null ? color() : color); - int x1 = 5; - int y1 = num(5, height - 5); - int x2 = width - 5; - int y2 = num(5, height - 5); - int ctrlx = num(5, width - 5); - int ctrly = num(5, height - 5); + int x1 = 5, y1 = num(5, height / 2); + int x2 = width - 5, y2 = num(height / 2, height - 5); + int ctrlx = num(width / 4, width / 4 * 3), ctrly = num(5, height - 5); + if (num(2) == 0) { + int ty = y1; + y1 = y2; + y2 = ty; + } if (num(2) == 0) { // 二阶贝塞尔曲线 QuadCurve2D shape = new QuadCurve2D.Double(); shape.setCurve(x1, y1, ctrlx, ctrly, x2, y2); g.draw(shape); } else { // 三阶贝塞尔曲线 - int ctrlx1 = num(5, width - 5); - int ctrly1 = num(5, height - 5); + int ctrlx1 = num(width / 4, width / 4 * 3), ctrly1 = num(5, height - 5); CubicCurve2D shape = new CubicCurve2D.Double(x1, y1, ctrlx, ctrly, ctrlx1, ctrly1, x2, y2); g.draw(shape); } @@ -247,7 +263,11 @@ public abstract class Captcha extends Randoms { } public void setFont(int font) throws IOException, FontFormatException { - setFont(font, Font.BOLD, 32f); + setFont(font, 32f); + } + + public void setFont(int font, float size) throws IOException, FontFormatException { + setFont(font, Font.BOLD, size); } public void setFont(int font, int style, float size) throws IOException, FontFormatException { diff --git a/src/main/java/com/wf/captcha/base/ChineseCaptchaAbstract.java b/src/main/java/com/wf/captcha/base/ChineseCaptchaAbstract.java index bd3a11a..f013edb 100644 --- a/src/main/java/com/wf/captcha/base/ChineseCaptchaAbstract.java +++ b/src/main/java/com/wf/captcha/base/ChineseCaptchaAbstract.java @@ -1,7 +1,5 @@ package com.wf.captcha.base; -import com.wf.captcha.base.Captcha; - import java.awt.*; public abstract class ChineseCaptchaAbstract extends Captcha { @@ -37,16 +35,4 @@ public abstract class ChineseCaptchaAbstract extends Captcha { return DELTA.charAt(num(DELTA.length())); } - - @Override - public void drawLine(int num, Color color, Graphics2D g) { - for (int i = 0; i < num; i++) { - g.setColor(color == null ? color(150, 250) : color); - int x1 = num(-10, width / 3); - int y1 = num(5, height - 5); - int x2 = num(width / 3 * 2, width + 10); - int y2 = num(2, height - 2); - g.drawLine(x1, y1, x2, y2); - } - } } diff --git a/src/main/java/com/wf/captcha/utils/CaptchaUtil.java b/src/main/java/com/wf/captcha/utils/CaptchaUtil.java index 0c3595e..2f04b54 100644 --- a/src/main/java/com/wf/captcha/utils/CaptchaUtil.java +++ b/src/main/java/com/wf/captcha/utils/CaptchaUtil.java @@ -7,7 +7,6 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.wf.captcha.base.Captcha; -import com.wf.captcha.GifCaptcha; import com.wf.captcha.SpecCaptcha; /** @@ -16,21 +15,9 @@ import com.wf.captcha.SpecCaptcha; */ public class CaptchaUtil { private static final String SESSION_KEY = "captcha"; - - /** - * 验证验证码 - * - * @param code 用户输入的验证码 - * @param request HttpServletRequest - * @return 是否正确 - */ - public static boolean ver(String code, HttpServletRequest request) { - if (code != null && !code.trim().isEmpty()) { - String captcha = (String) request.getSession().getAttribute(SESSION_KEY); - return code.trim().toLowerCase().equals(captcha); - } - return false; - } + private static final int DEFAULT_LEN = 4; // 默认长度 + private static final int DEFAULT_WIDTH = 130; // 默认宽度 + private static final int DEFAULT_HEIGHT = 48; // 默认高度 /** * 输出验证码 @@ -41,7 +28,7 @@ public class CaptchaUtil { */ public static void out(HttpServletRequest request, HttpServletResponse response) throws IOException { - out(5, request, response); + out(DEFAULT_LEN, request, response); } /** @@ -54,21 +41,7 @@ public class CaptchaUtil { */ public static void out(int len, HttpServletRequest request, HttpServletResponse response) throws IOException { - out(130, 48, len, request, response); - } - - /** - * 输出验证码 - * - * @param len 长度 - * @param font 字体 - * @param request HttpServletRequest - * @param response HttpServletResponse - * @throws IOException IO异常 - */ - public static void out(int len, Font font, HttpServletRequest request, HttpServletResponse response) - throws IOException { - out(130, 48, len, font, request, response); + out(DEFAULT_WIDTH, DEFAULT_HEIGHT, len, request, response); } /** @@ -89,42 +62,14 @@ public class CaptchaUtil { /** * 输出验证码 * - * @param width 宽度 - * @param height 高度 - * @param len 长度 * @param font 字体 * @param request HttpServletRequest * @param response HttpServletResponse * @throws IOException IO异常 */ - public static void out(int width, int height, int len, Font font, HttpServletRequest request, HttpServletResponse response) + public static void out(Font font, HttpServletRequest request, HttpServletResponse response) throws IOException { - outCaptcha(width, height, len, font, 1, request, response); - } - - /** - * 输出验证码 - * - * @param request HttpServletRequest - * @param response HttpServletResponse - * @throws IOException IO异常 - */ - public static void outPng(HttpServletRequest request, HttpServletResponse response) - throws IOException { - outPng(5, request, response); - } - - /** - * 输出验证码 - * - * @param len 长度 - * @param request HttpServletRequest - * @param response HttpServletResponse - * @throws IOException IO异常 - */ - public static void outPng(int len, HttpServletRequest request, HttpServletResponse response) - throws IOException { - outPng(130, 48, len, request, response); + out(DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_LEN, font, request, response); } /** @@ -136,24 +81,9 @@ public class CaptchaUtil { * @param response HttpServletResponse * @throws IOException IO异常 */ - public static void outPng(int len, Font font, HttpServletRequest request, HttpServletResponse response) + public static void out(int len, Font font, HttpServletRequest request, HttpServletResponse response) throws IOException { - outPng(130, 48, len, font, request, response); - } - - /** - * 输出验证码 - * - * @param width 宽度 - * @param height 高度 - * @param len 长度 - * @param request HttpServletRequest - * @param response HttpServletResponse - * @throws IOException IO异常 - */ - public static void outPng(int width, int height, int len, HttpServletRequest request, HttpServletResponse response) - throws IOException { - outPng(width, height, len, null, request, response); + out(DEFAULT_WIDTH, DEFAULT_HEIGHT, len, font, request, response); } /** @@ -167,39 +97,46 @@ public class CaptchaUtil { * @param response HttpServletResponse * @throws IOException IO异常 */ - public static void outPng(int width, int height, int len, Font font, HttpServletRequest request, HttpServletResponse response) + public static void out(int width, int height, int len, Font font, HttpServletRequest request, HttpServletResponse response) throws IOException { - outCaptcha(width, height, len, font, 0, request, response); + SpecCaptcha specCaptcha = new SpecCaptcha(width, height, len); + if (font != null) { + specCaptcha.setFont(font); + } + out(specCaptcha, request, response); } + /** * 输出验证码 * - * @param width 宽度 - * @param height 高度 - * @param len 长度 - * @param font 字体 - * @param cType 类型 + * @param captcha Captcha * @param request HttpServletRequest * @param response HttpServletResponse * @throws IOException IO异常 */ - private static void outCaptcha(int width, int height, int len, Font font, int cType, HttpServletRequest request, HttpServletResponse response) + private static void out(Captcha captcha, HttpServletRequest request, HttpServletResponse response) throws IOException { setHeader(response); - Captcha captcha = null; - if (cType == 0) { - captcha = new SpecCaptcha(width, height, len); - } else if (cType == 1) { - captcha = new GifCaptcha(width, height, len); - } - if (font != null) { - captcha.setFont(font); - } request.getSession().setAttribute(SESSION_KEY, captcha.text().toLowerCase()); captcha.out(response.getOutputStream()); } + /** + * 验证验证码 + * + * @param code 用户输入的验证码 + * @param request HttpServletRequest + * @return 是否正确 + */ + public static boolean ver(String code, HttpServletRequest request) { + if (code != null) { + String captcha = (String) request.getSession().getAttribute(SESSION_KEY); + return code.trim().toLowerCase().equals(captcha); + } + return false; + } + /** * 清除session中的验证码 * diff --git a/src/test/java/com/wf/captcha/CaptchaTest.java b/src/test/java/com/wf/captcha/CaptchaTest.java index bde5244..d4ee3d5 100644 --- a/src/test/java/com/wf/captcha/CaptchaTest.java +++ b/src/test/java/com/wf/captcha/CaptchaTest.java @@ -1,15 +1,9 @@ package com.wf.captcha; -import com.wf.captcha.base.Captcha; import org.junit.Test; -import javax.imageio.ImageIO; -import java.awt.*; -import java.awt.geom.QuadCurve2D; -import java.awt.image.BufferedImage; import java.io.File; import java.io.FileOutputStream; -import java.io.IOException; /** * 测试类 @@ -19,98 +13,59 @@ public class CaptchaTest { @Test public void test() throws Exception { - for (int i = 0; i < 5; i++) { + /*for (int i = 0; i < 10; i++) { SpecCaptcha specCaptcha = new SpecCaptcha(); - // specCaptcha.setCharType(Captcha.TYPE_ONLY_UPPER); + specCaptcha.setLen(4); + specCaptcha.setFont(i, 32f); System.out.println(specCaptcha.text()); - // specCaptcha.setFont(Font.createFont(Font.TRUETYPE_FONT, new File(getClass().getResource("/actionj.ttf").getFile())).deriveFont(Font.BOLD, 32)); - specCaptcha.out(new FileOutputStream(new File("D:/Java/aa" + i + ".png"))); - } + specCaptcha.out(new FileOutputStream(new File("C:/Java/aa" + i + ".png"))); + }*/ } @Test public void testGIf() throws Exception { - /*for (int i = 0; i < 5; i++) { + /*for (int i = 0; i < 10; i++) { GifCaptcha gifCaptcha = new GifCaptcha(); + gifCaptcha.setLen(5); + gifCaptcha.setFont(i, 32f); System.out.println(gifCaptcha.text()); - gifCaptcha.out(new FileOutputStream(new File("D:/Java/aa" + i + ".gif"))); + gifCaptcha.out(new FileOutputStream(new File("C:/Java/aa" + i + ".gif"))); }*/ } @Test public void testHan() throws Exception { - /*for (int i = 0; i < 5; i++) { + /*for (int i = 0; i < 10; i++) { ChineseCaptcha chineseCaptcha = new ChineseCaptcha(); System.out.println(chineseCaptcha.text()); - chineseCaptcha.out(new FileOutputStream(new File("D:/Java/aa" + i + ".png"))); + chineseCaptcha.out(new FileOutputStream(new File("C:/Java/aa" + i + ".png"))); }*/ } @Test public void testGifHan() throws Exception { - /*for (int i = 0; i < 5; i++) { + /*for (int i = 0; i < 10; i++) { ChineseGifCaptcha chineseGifCaptcha = new ChineseGifCaptcha(); System.out.println(chineseGifCaptcha.text()); - chineseGifCaptcha.out(new FileOutputStream(new File("D:/Java/aa" + i + ".gif"))); + chineseGifCaptcha.out(new FileOutputStream(new File("C:/Java/aa" + i + ".gif"))); }*/ } @Test - public void test2() throws Exception { - String[] colors = new String[]{"#8cc540", "#009f5d", "#019fa0", "#019fde", "#007cdc", "#887ddd", "#cd7bdd", "#ff5675", "#ff1244", "#ff8345", "#f8bd0b", "#d1d2d4"}; - String[] fonts = new String[]{"actionj.ttf", "epilog.ttf", "fresnel.ttf", "headache.ttf", "lexo.ttf", "prefix.ttf", "progbot.ttf", "ransom.ttf", "robot.ttf", "scandal.ttf"}; - int i = 0; - for (String f : fonts) { - i++; - FileOutputStream out = new FileOutputStream(new File("D:/Java/aa" + i + ".png")); - try { - int width = 130, height = 48; - BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); - Graphics2D g = (Graphics2D) bi.getGraphics(); - g.setColor(Color.WHITE); - g.fillRect(0, 0, width, height); - // 抗锯齿 - g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - g.setStroke(new BasicStroke(1.3f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); - // 画字符串 - float fontSize = 32f; - Font font = Font.createFont(Font.TRUETYPE_FONT, new File(getClass().getResource("/" + f).getFile())).deriveFont(Font.BOLD, fontSize); - g.setFont(font); - char[] chars = "N4WbS".toCharArray(); - float sp = (width / chars.length - fontSize) / 2; - int j = 0; - for (char c : chars) { - j++; - g.setColor(Color.getColor(colors[j])); - float x = sp * j + fontSize * (j - 1); - float y = (height - fontSize) / 2 + fontSize; - System.out.println(x + "-" + y); - g.drawString(String.valueOf(c), x, y); - } - g.drawString("Hello", 8, 37); - // - QuadCurve2D shape = new QuadCurve2D.Double(); - shape.setCurve(8, 38, 38, 10, 120, 40); - g.draw(shape); - ImageIO.write(bi, "png", out); - out.flush(); - } catch (IOException e) { - e.printStackTrace(); - } finally { - try { - out.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - } + public void testArit() throws Exception { + /*for (int i = 0; i < 10; i++) { + ArithmeticCaptcha specCaptcha = new ArithmeticCaptcha(); + specCaptcha.setLen(3); + specCaptcha.setFont(i, 28f); + System.out.println(specCaptcha.getArithmeticString() + " " + specCaptcha.text()); + specCaptcha.out(new FileOutputStream(new File("C:/Java/aa" + i + ".png"))); + }*/ } @Test - public void test3() throws Exception { - String filePath = getClass().getResource("/actionj.ttf").getFile(); - System.out.println(filePath); + public void testBase64() throws Exception { + /*GifCaptcha specCaptcha = new GifCaptcha(); + System.out.println(specCaptcha.toBase64(""));*/ } - }