1
0
mirror of https://github.com/whvcse/EasyCaptcha.git synced 2024-11-23 01:59:19 +08:00

增加内置字体、贝塞尔曲线、算术验证码、base64输出

This commit is contained in:
whvcse@foxmail.com 2019-08-23 23:50:26 +08:00
parent 366f54ea80
commit 8ebe5918c9
12 changed files with 640 additions and 527 deletions

402
README.md
View File

@ -5,48 +5,54 @@
## 1.简介 ## 1.简介
 Java图形验证码支持gif、中文、算术等类型可用于Java Web、JavaSE等项目。
  Java图形验证码支持gif验证码、中文验证码可用于Java Web、JavaSE项目。 ---
## 2.效果展示 ## 2.效果展示
**gif效果** ![验证码](https://s2.ax1x.com/2019/08/23/msFrE8.png)
![验证码](https://ws1.sinaimg.cn/large/006a7GCKly1ftpd30vgc2g303m01c0st.jpg)
     
![验证码](https://ws1.sinaimg.cn/large/006a7GCKly1ftpd22ixh9g303m01c3yj.jpg) ![验证码](https://s2.ax1x.com/2019/08/23/msF0DP.png)
     
![验证码](https://ws1.sinaimg.cn/large/006a7GCKly1ftpd2k0gjug303m01ct8t.jpg) ![验证码](https://s2.ax1x.com/2019/08/23/msFwut.png)
<br/>
**png效果** ![验证码](https://s2.ax1x.com/2019/08/23/msFzVK.gif)
![验证码](https://ws1.sinaimg.cn/large/006a7GCKly1ftpd09lqpaj303m01cwee.jpg)
&emsp;&emsp; &emsp;&emsp;
![验证码](https://ws1.sinaimg.cn/large/006a7GCKly1ftpd0tj6nhj303m01ct8m.jpg) ![验证码](https://s2.ax1x.com/2019/08/23/msFvb6.gif)
&emsp;&emsp; &emsp;&emsp;
![验证码](https://ws1.sinaimg.cn/large/006a7GCKly1ftpd1cw3pcj303m01c0sn.jpg) ![验证码](https://s2.ax1x.com/2019/08/23/msFXK1.gif)
**算术类型:**
**中文验证码:** ![验证码](https://s2.ax1x.com/2019/08/23/mskKPg.png)
![验证码](https://ws1.sinaimg.cn/large/006a7GCKly1ftovnvbgx6g303m01cjrc.jpg)
&emsp;&emsp; &emsp;&emsp;
![验证码](https://ws1.sinaimg.cn/large/006a7GCKly1ftovojp2gng303m01cq2w.jpg) ![验证码](https://s2.ax1x.com/2019/08/23/msknIS.png)
&emsp;&emsp; &emsp;&emsp;
![验证码](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)
&emsp;&emsp; &emsp;&emsp;
![验证码](https://ws1.sinaimg.cn/large/006a7GCKly1ftovmy720dj303m01cq2s.jpg) ![验证码](https://s2.ax1x.com/2019/08/23/msk6Z6.png)
&emsp;&emsp; &emsp;&emsp;
![验证码](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)
&emsp;&emsp;
![验证码](https://s2.ax1x.com/2019/08/23/msAAW4.png)
&emsp;&emsp;
![验证码](https://s2.ax1x.com/2019/08/23/msAkYF.png)
---
## 3.导入项目 ## 3.导入项目
### 2.1.gradle方式的引入 ### 3.1.gradle方式的引入
需要先在project的build.gradle中添加 需要先在project的build.gradle中添加
```text ```text
allprojects { allprojects {
@ -58,11 +64,11 @@ allprojects {
在项目的build.gradle中添加 在项目的build.gradle中添加
```text ```text
dependencies { 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中添加如下代码 在你的pom.xml中添加如下代码
```xml ```xml
<project> <project>
@ -77,33 +83,49 @@ dependencies {
<dependency> <dependency>
<groupId>com.github.whvcse</groupId> <groupId>com.github.whvcse</groupId>
<artifactId>EasyCaptcha</artifactId> <artifactId>EasyCaptcha</artifactId>
<version>1.5.0</version> <version>1.6.0</version>
</dependency> </dependency>
</dependencies> </dependencies>
</project> </project>
``` ```
### 2.3.jar包下载 ### 3.3.jar包下载
[EasyCaptcha-1.5.0.jar](https://gitee.com/whvse/EasyCaptcha/releases) [EasyCaptcha-1.6.0.jar](https://gitee.com/whvse/EasyCaptcha/releases)
maven导入jar包在项目根目录创建`libs`文件夹然后pom.xml添加如下 maven导入jar包在项目根目录创建`libs`文件夹然后pom.xml添加如下
``` ```
<dependency> <dependency>
<groupId>com.github.whvcse</groupId> <groupId>com.github.whvcse</groupId>
<artifactId>EasyCaptcha</artifactId> <artifactId>EasyCaptcha</artifactId>
<version>1.5.0</version> <version>1.6.0</version>
<systemPath>${basedir}/libs/EasyCaptcha-1.5.0.jar</systemPath> <systemPath>${basedir}/libs/EasyCaptcha-1.6.0.jar</systemPath>
</dependency> </dependency>
``` ```
--- ---
## 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
<img src="/captcha" width="130px" height="48px" />
```
### 3.1.快速使用 > 不要忘了把`/captcha`路径排除登录拦截比如shiro的拦截。
1.在web.xml里面加入如下配置
### 4.2.在servlet中使用
web.xml中配置servlet
```xml ```xml
<web-app> <web-app>
<!-- 图形验证码servlet --> <!-- 图形验证码servlet -->
@ -113,163 +135,130 @@ maven导入jar包在项目根目录创建`libs`文件夹然后pom.xml添
</servlet> </servlet>
<servlet-mapping> <servlet-mapping>
<servlet-name>CaptchaServlet</servlet-name> <servlet-name>CaptchaServlet</servlet-name>
   <url-pattern>/images/captcha</url-pattern>    <url-pattern>/captcha</url-pattern>
</servlet-mapping> </servlet-mapping>
</web-app> </web-app>
``` ```
2.前端代码 前端html代码
```html ```html
<img src="/images/captcha" /> <img src="/captcha" width="130px" height="48px" />
``` ```
### 3.2.在SpringMVC中使用 ### 4.3.判断验证码是否正确
也可以使用controller的形式输出验证码方法如下
```java
@Controller
public class MainController {
@RequestMapping("/images/captcha")
public void captcha(HttpServletRequest request, HttpServletResponse response) throws Exception {
CaptchaUtil.out(request, response);
}
}
```
前端代码:
```html
<img src="/images/captcha" />
```
### 3.3.判断验证码是否正确
```java ```java
@Controller @Controller
public class LoginController { public class LoginController {
@PostMapping("/login") @PostMapping("/login")
public JsonResult login(String username,String password,String code){ public JsonResult login(String username,String password,String verCode){
if (!CaptchaUtil.ver(verCode, request)) {
if (!CaptchaUtil.ver(code, request)) { CaptchaUtil.clear(request); // 清除session中的验证码
CaptchaUtil.clear(request);
return JsonResult.error("验证码不正确"); return JsonResult.error("验证码不正确");
} }
} }
} }
``` ```
### 3.4.设置宽高和位数 ### 4.4.设置宽高和位数
```java ```java
@Controller @Controller
public class MainController { public class CaptchaController {
@RequestMapping("/images/captcha") @RequestMapping("/captcha")
public void captcha(HttpServletRequest request, HttpServletResponse response) throws Exception { public void captcha(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 设置位数 // 设置位数
CaptchaUtil.out(5, request, response); CaptchaUtil.out(5, request, response);
// 设置宽、高、位数 // 设置宽、高、位数
CaptchaUtil.out(130, 48, 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.不使用工具类 ### 4.5.不使用工具类
&emsp;CaptchaUtil封装了输出验证码、存session、判断验证码等功能也可以不使用此工具类
&emsp;&emsp;CaptchaUtil是为了简化操作封装了生成验证码、存session、判断验证码等功能。CaptchaUtil使用的GifCaptcha
生成的字母数字混合的gif验证码如果需要设置更多的参数请参照如下操作使用
```java ```java
@Controller @Controller
public class MainController { public class CaptchaController {
@RequestMapping("/images/captcha") @RequestMapping("/captcha")
public void captcha(HttpServletRequest request, HttpServletResponse response) throws Exception { 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 // 验证码存入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") @PostMapping("/login")
public JsonResult login(String username,String password,String code){ public JsonResult login(String username,String password,String verCode){
// 获取session中的验证码 // 获取session中的验证码
String sessionCode = request.getSession().getAttribute("captcha"); 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("验证码不正确"); return JsonResult.error("验证码不正确");
} }
} }
} }
``` ```
## 4.更多设置 ## 5.更多设置
### 4.1.使用Gif验证码 ### 5.1.验证码类型
```java ```java
public class Test { public class Test {
public static void main(String[] args) { 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(); // 获取验证码的字符数组
// 三个参数分别为宽、高、位数 // gif类型
GifCaptcha gifCaptcha = new GifCaptcha(130, 48, 5); GifCaptcha captcha = new GifCaptcha(130, 48, 5);
// 设置字体 // 中文类型
gifCaptcha.setFont(new Font("Verdana", Font.PLAIN, 32)); // 有默认字体,可以不用设置 ChineseCaptcha captcha = new ChineseCaptcha(130, 48, 5);
// 设置类型,纯数字、纯字母、字母数字混合 // 中文gif类型
gifCaptcha.setCharType(Captcha.TYPE_ONLY_NUMBER); 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
// 输出图片流 captcha.out(outputStream); // 输出验证码
gifCaptcha.out(outputStream);
} }
} }
``` ```
### 4.2.使用png验证码 > 注意:<br/>
> &emsp;算术验证码的len表示是几位数运算而其他验证码的len表示验证码的位数算术验证码的text()表示的是公式的结果,
> 对于算术验证码你应该把公式的结果存储session而不是公式。
```java ### 5.2.验证码字符类型
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.验证码类型
类型 | 描述 类型 | 描述
:--- | :--- :--- | :---
@ -280,60 +269,145 @@ public class Test {
TYPE_ONLY_LOWER | 纯小写字母 TYPE_ONLY_LOWER | 纯小写字母
TYPE_NUM_AND_UPPER | 数字和大写字母 TYPE_NUM_AND_UPPER | 数字和大写字母
使用方法:
### 4.4.中文验证码 ```
SpecCaptcha captcha = new SpecCaptcha(130, 48, 5);
中文png验证码 captcha.setCharType(Captcha.TYPE_ONLY_NUMBER);
```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);
}
}
``` ```
中文gif验证码 > 只有`SpecCaptcha`和`GifCaptcha`设置才有效果。
```java ### 5.3.字体设置
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)); // 有默认字体,可以不用设置
// 生成的验证码 字体 | 效果
String code = chineseGifCaptcha.text(); :--- | :---
Captcha.FONT_1 | ![](https://s2.ax1x.com/2019/08/23/msMe6U.png)
// 输出图片流 Captcha.FONT_2 | ![](https://s2.ax1x.com/2019/08/23/msMAf0.png)
chineseGifCaptcha.out(outputStream); 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();
&emsp;&emsp;分离项目建议不要存储在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.前后端分离项目的使用
&emsp;前后端分离项目建议不要存储在session中存储在redis中redis存储需要一个keykey一同返回给前端用于验证输入
```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
<img id="verImg" />
<script>
var verKey;
// 获取验证码
$.get('/captcha', function(res) {
verKey = res.key;
$('#verImg').attr('src', res.image);
},'json');
// 登录
$.post('/login', {
verKey: verKey,
verCode: '8u6h',
username: 'admin'
password: 'admin'
}, function(res) {
console.log(res);
}, 'json');
</script>
```
> RedisUtil到这里获取[https://gitee.com/whvse/RedisUtil](https://gitee.com/whvse/RedisUtil)
---
## 7.自定义效果
&emsp;继承`Captcha`实现`out`方法,中文验证码可继承`ChineseCaptchaAbstract`,算术验证码可继承`ArithmeticCaptchaAbstract`。
---
## 8.更新日志
- **2019-08-23 (v1.6.0)**
- 增加10种漂亮的内置字体不依赖系统字体
- 增加算术验证码,运算位数可自由配置
- 增加输出base64编码的功能
- 增加贝塞尔曲线作为干扰线
- **2018-08-09 (v1.5.0)**
- 增加纯大写字母、纯小写字母、数字和大写字母配置
- 增加中文验证码、中文gif验证码
- 增加抗锯齿效果,优化文字颜色
- 增加CaptchaUtil便于Web项目使用
---
## 9.推荐
&emsp;**EasyWeb管系统模板**一个开箱即用的后台模板使用简单功能丰富包含ifram版和spa单页面版[前往查看](https://easyweb.vip)。
![](https://s2.ax1x.com/2019/08/23/msiO78.png)

View File

@ -5,7 +5,7 @@
<groupId>org.wf</groupId> <groupId>org.wf</groupId>
<artifactId>easy-captcha</artifactId> <artifactId>easy-captcha</artifactId>
<version>1.5.0-RELEASE</version> <version>1.6.0-RELEASE</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>EasyCaptcha</name> <name>EasyCaptcha</name>
@ -23,7 +23,7 @@
<dependency> <dependency>
<groupId>junit</groupId> <groupId>junit</groupId>
<artifactId>junit</artifactId> <artifactId>junit</artifactId>
<version>4.7</version> <version>4.12</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>

View File

@ -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;
}
}

View File

@ -38,10 +38,14 @@ public class ChineseCaptcha extends ChineseCaptchaAbstract {
*/ */
@Override @Override
public boolean out(OutputStream out) { public boolean out(OutputStream out) {
checkAlpha();
return graphicsImage(textChar(), out); 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 * @return boolean
*/ */
private boolean graphicsImage(char[] strs, OutputStream out) { private boolean graphicsImage(char[] strs, OutputStream out) {
boolean ok;
try { try {
BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g = (Graphics2D) bi.getGraphics(); Graphics2D g2d = (Graphics2D) bi.getGraphics();
AlphaComposite ac3; // 填充背景
int len = strs.length; g2d.setColor(Color.WHITE);
g.setColor(Color.WHITE); g2d.fillRect(0, 0, width, height);
g.fillRect(0, 0, width, height);
// 抗锯齿 // 抗锯齿
g.setColor(color()); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
Font font = getFont(); // 画干扰圆
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); drawOval(3, g2d);
int hp = (height - font.getSize()) >> 1; // 画干扰线
int h = height - hp; g2d.setStroke(new BasicStroke(1.2f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
int w = width / strs.length; drawBesselLine(1, g2d);
int sp = (w - font.getSize()) / 2;
// 画字符串 // 画字符串
for (int i = 0; i < len; i++) { g2d.setFont(getFont());
// 计算坐标 FontMetrics fontMetrics = g2d.getFontMetrics();
int x = i * w + sp + num(-5, 5); int fW = width / strs.length; // 每一个字符所占的宽度
int y = h + num(-5, 5); int fSp = (fW - (int) fontMetrics.getStringBounds("", g2d).getWidth()) / 2; // 字符的左右边距
if (x < 5) { for (int i = 0; i < strs.length; i++) {
x = 5; g2d.setColor(color());
} int fY = height - ((height - (int) fontMetrics.getStringBounds(String.valueOf(strs[i]), g2d).getHeight()) >> 1); // 文字的纵坐标
if (x + font.getSize() > width) { g2d.drawString(String.valueOf(strs[i]), i * fW + fSp + 3, fY - 3);
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.dispose();
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);
ImageIO.write(bi, "png", out); ImageIO.write(bi, "png", out);
out.flush(); out.flush();
ok = true; return true;
} catch (IOException e) { } catch (IOException e) {
ok = false;
e.printStackTrace(); e.printStackTrace();
} finally { } finally {
try { try {
@ -106,6 +90,6 @@ public class ChineseCaptcha extends ChineseCaptchaAbstract {
e.printStackTrace(); e.printStackTrace();
} }
} }
return ok; return false;
} }
} }

View File

@ -4,6 +4,7 @@ import com.wf.captcha.base.ChineseCaptchaAbstract;
import com.wf.captcha.utils.GifEncoder; import com.wf.captcha.utils.GifEncoder;
import java.awt.*; import java.awt.*;
import java.awt.geom.CubicCurve2D;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
@ -30,24 +31,39 @@ public class ChineseGifCaptcha extends ChineseCaptchaAbstract {
@Override @Override
public boolean out(OutputStream os) { public boolean out(OutputStream os) {
checkAlpha();
boolean ok;
try { 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 gifEncoder = new GifEncoder();
gifEncoder.start(os);
gifEncoder.setQuality(180); gifEncoder.setQuality(180);
gifEncoder.setDelay(100); gifEncoder.setDelay(100);
gifEncoder.setRepeat(0); gifEncoder.setRepeat(0);
BufferedImage frame; gifEncoder.start(os);
Color fontcolor = color();
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
frame = graphicsImage(fontcolor, rands, i); BufferedImage frame = graphicsImage(fontColor, strs, i, besselXY);
gifEncoder.addFrame(frame); gifEncoder.addFrame(frame);
frame.flush(); frame.flush();
} }
gifEncoder.finish(); gifEncoder.finish();
ok = true; return true;
} catch (Exception e) {
e.printStackTrace();
} finally { } finally {
try { try {
os.close(); os.close();
@ -55,60 +71,53 @@ public class ChineseGifCaptcha extends ChineseCaptchaAbstract {
e.printStackTrace(); e.printStackTrace();
} }
} }
return ok; return false;
}
@Override
public String toBase64() {
return toBase64("data:image/gif;base64,");
} }
/** /**
* 画随机码图 * 画随机码图
* *
* @param fontcolor 随机字体颜色 * @param fontColor 随机字体颜色
* @param strs 字符数组 * @param strs 字符数组
* @param flag 透明度使用 * @param flag 透明度
* @param besselXY 干扰线参数
* @return BufferedImage * @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); BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = (Graphics2D) image.getGraphics(); Graphics2D g2d = (Graphics2D) image.getGraphics();
g2d.setColor(Color.WHITE); // 填充背景颜色 // 填充背景颜色
g2d.setColor(Color.WHITE);
g2d.fillRect(0, 0, width, height); g2d.fillRect(0, 0, width, height);
// 抗锯齿 // 抗锯齿
AlphaComposite ac3;
g2d.setColor(fontcolor);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 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(); g2d.dispose();
return image; return image;
} }

View File

@ -4,6 +4,9 @@ import com.wf.captcha.base.Captcha;
import com.wf.captcha.utils.GifEncoder; import com.wf.captcha.utils.GifEncoder;
import java.awt.*; 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.awt.image.BufferedImage;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
@ -34,27 +37,39 @@ public class GifCaptcha extends Captcha {
@Override @Override
public boolean out(OutputStream os) { public boolean out(OutputStream os) {
checkAlpha();
boolean ok;
try { 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 gifEncoder = new GifEncoder();
gifEncoder.start(os);
gifEncoder.setQuality(180); gifEncoder.setQuality(180);
gifEncoder.setDelay(100); gifEncoder.setDelay(100);
gifEncoder.setRepeat(0); gifEncoder.setRepeat(0);
BufferedImage frame; gifEncoder.start(os);
Color fontcolor[] = new Color[len];
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
fontcolor[i] = new Color(20 + num(110), 20 + num(110), 20 + num(110)); BufferedImage frame = graphicsImage(fontColor, strs, i, besselXY);
}
for (int i = 0; i < len; i++) {
frame = graphicsImage(fontcolor, rands, i);
gifEncoder.addFrame(frame); gifEncoder.addFrame(frame);
frame.flush(); frame.flush();
} }
gifEncoder.finish(); gifEncoder.finish();
ok = true; return true;
} catch (Exception e) {
e.printStackTrace();
} finally { } finally {
try { try {
os.close(); os.close();
@ -62,18 +77,24 @@ public class GifCaptcha extends Captcha {
e.printStackTrace(); e.printStackTrace();
} }
} }
return ok; return false;
}
@Override
public String toBase64() {
return toBase64("data:image/gif;base64,");
} }
/** /**
* 画随机码图 * 画随机码图
* *
* @param fontcolor 随机字体颜色 * @param fontColor 随机字体颜色
* @param strs 字符数组 * @param strs 字符数组
* @param flag 透明度使用 * @param flag 透明度
* @param besselXY 干扰线参数
* @return BufferedImage * @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); BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = (Graphics2D) image.getGraphics(); Graphics2D g2d = (Graphics2D) image.getGraphics();
// 填充背景颜色 // 填充背景颜色
@ -81,39 +102,27 @@ public class GifCaptcha extends Captcha {
g2d.fillRect(0, 0, width, height); g2d.fillRect(0, 0, width, height);
// 抗锯齿 // 抗锯齿
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setStroke(new BasicStroke(1.3f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
// 画干扰圆圈 // 画干扰圆圈
drawOval(4, g2d); g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.1f * num(10))); // 设置透明度
// 随机画干扰线 drawOval(2, g2d);
drawLine(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(); g2d.setFont(getFont());
int hp = (height - font.getSize()) >> 1; FontMetrics fontMetrics = g2d.getFontMetrics();
int h = height - hp; int fW = width / strs.length; // 每一个字符所占的宽度
int w = width / strs.length; int fSp = (fW - (int) fontMetrics.getStringBounds("W", g2d).getWidth()) / 2; // 字符的左右边距
int sp = (w - font.getSize()) / 2;
for (int i = 0; i < strs.length; i++) { for (int i = 0; i < strs.length; i++) {
// 设置透明度
AlphaComposite ac3 = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, getAlpha(flag, i)); AlphaComposite ac3 = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, getAlpha(flag, i));
g2d.setComposite(ac3); g2d.setComposite(ac3);
g2d.setColor(fontcolor[i]); g2d.setColor(fontColor[i]);
// 计算坐标 int fY = height - ((height - (int) fontMetrics.getStringBounds(String.valueOf(strs[i]), g2d).getHeight()) >> 1); // 文字的纵坐标
int x = i * w + sp + num(3); g2d.drawString(String.valueOf(strs[i]), i * fW + fSp + 3, fY - 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.dispose(); g2d.dispose();
return image; return image;

View File

@ -45,6 +45,11 @@ public class SpecCaptcha extends Captcha {
return graphicsImage(textChar(), out); 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) { private boolean graphicsImage(char[] strs, OutputStream out) {
try { try {
BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g = (Graphics2D) bi.getGraphics(); Graphics2D g2d = (Graphics2D) bi.getGraphics();
// 填充背景 // 填充背景
g.setColor(Color.WHITE); g2d.setColor(Color.WHITE);
g.fillRect(0, 0, width, height); g2d.fillRect(0, 0, width, height);
// 抗锯齿 // 抗锯齿
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.setStroke(new BasicStroke(1.3f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); // 画干扰圆
// 随机画干扰线 drawOval(2, g2d);
// drawLine(3, g); // 画干扰线
// 随机画干扰圆 g2d.setStroke(new BasicStroke(2f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
// drawOval(8, g); drawBesselLine(1, g2d);
drawBesselLine(2, g);
// 画字符串 // 画字符串
// AlphaComposite ac3 = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.8f);// 指定透明度 g2d.setFont(getFont());
// g.setComposite(ac3); FontMetrics fontMetrics = g2d.getFontMetrics();
g.setFont(getFont());
int fontSize = getFont().getSize();
int fY = height - ((height - fontSize) >> 1); // 文字的纵坐标
int fW = width / strs.length; // 每一个字符所占的宽度 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++) { for (int i = 0; i < strs.length; i++) {
g.setColor(color()); g2d.setColor(color());
// 计算坐标 int fY = height - ((height - (int) fontMetrics.getStringBounds(String.valueOf(strs[i]), g2d).getHeight()) >> 1); // 文字的纵坐标
int x = i * fW + fSp; g2d.drawString(String.valueOf(strs[i]), i * fW + fSp + 3, fY - 3);
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.dispose();
ImageIO.write(bi, "png", out); ImageIO.write(bi, "png", out);
out.flush(); out.flush();
return true; return true;

View File

@ -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;
}
}

View File

@ -1,8 +1,11 @@
package com.wf.captcha.base; package com.wf.captcha.base;
import sun.misc.BASE64Encoder;
import java.awt.*; import java.awt.*;
import java.awt.geom.CubicCurve2D; import java.awt.geom.CubicCurve2D;
import java.awt.geom.QuadCurve2D; import java.awt.geom.QuadCurve2D;
import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
@ -108,6 +111,20 @@ public abstract class Captcha extends Randoms {
*/ */
public abstract boolean out(OutputStream os); 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) { public void drawLine(int num, Color color, Graphics2D g) {
for (int i = 0; i < num; i++) { 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 x1 = num(-10, width - 10);
int y1 = num(5, height - 5); int y1 = num(5, height - 5);
int x2 = num(10, width + 10); int x2 = num(10, width + 10);
@ -172,10 +189,7 @@ public abstract class Captcha extends Randoms {
* @param g Graphics2D * @param g Graphics2D
*/ */
public void drawOval(int num, Graphics2D g) { public void drawOval(int num, Graphics2D g) {
for (int i = 0; i < num; i++) { drawOval(num, null, g);
g.setColor(color(100, 250));
g.drawOval(num(width), num(height), 10 + num(20), 10 + num(20));
}
} }
/** /**
@ -187,8 +201,9 @@ public abstract class Captcha extends Randoms {
*/ */
public void drawOval(int num, Color color, Graphics2D g) { public void drawOval(int num, Color color, Graphics2D g) {
for (int i = 0; i < num; i++) { for (int i = 0; i < num; i++) {
g.setColor(color == null ? color(100, 250) : color); g.setColor(color == null ? color() : color);
g.drawOval(num(width), num(height), 10 + num(20), 10 + num(20)); 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) { public void drawBesselLine(int num, Color color, Graphics2D g) {
for (int i = 0; i < num; i++) { for (int i = 0; i < num; i++) {
g.setColor(color == null ? color() : color); g.setColor(color == null ? color() : color);
int x1 = 5; int x1 = 5, y1 = num(5, height / 2);
int y1 = num(5, height - 5); int x2 = width - 5, y2 = num(height / 2, height - 5);
int x2 = width - 5; int ctrlx = num(width / 4, width / 4 * 3), ctrly = num(5, height - 5);
int y2 = num(5, height - 5); if (num(2) == 0) {
int ctrlx = num(5, width - 5); int ty = y1;
int ctrly = num(5, height - 5); y1 = y2;
y2 = ty;
}
if (num(2) == 0) { // 二阶贝塞尔曲线 if (num(2) == 0) { // 二阶贝塞尔曲线
QuadCurve2D shape = new QuadCurve2D.Double(); QuadCurve2D shape = new QuadCurve2D.Double();
shape.setCurve(x1, y1, ctrlx, ctrly, x2, y2); shape.setCurve(x1, y1, ctrlx, ctrly, x2, y2);
g.draw(shape); g.draw(shape);
} else { // 三阶贝塞尔曲线 } else { // 三阶贝塞尔曲线
int ctrlx1 = num(5, width - 5); int ctrlx1 = num(width / 4, width / 4 * 3), ctrly1 = num(5, height - 5);
int ctrly1 = num(5, height - 5);
CubicCurve2D shape = new CubicCurve2D.Double(x1, y1, ctrlx, ctrly, ctrlx1, ctrly1, x2, y2); CubicCurve2D shape = new CubicCurve2D.Double(x1, y1, ctrlx, ctrly, ctrlx1, ctrly1, x2, y2);
g.draw(shape); g.draw(shape);
} }
@ -247,7 +263,11 @@ public abstract class Captcha extends Randoms {
} }
public void setFont(int font) throws IOException, FontFormatException { 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 { public void setFont(int font, int style, float size) throws IOException, FontFormatException {

View File

@ -1,7 +1,5 @@
package com.wf.captcha.base; package com.wf.captcha.base;
import com.wf.captcha.base.Captcha;
import java.awt.*; import java.awt.*;
public abstract class ChineseCaptchaAbstract extends Captcha { public abstract class ChineseCaptchaAbstract extends Captcha {
@ -37,16 +35,4 @@ public abstract class ChineseCaptchaAbstract extends Captcha {
return DELTA.charAt(num(DELTA.length())); 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);
}
}
} }

View File

@ -7,7 +7,6 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import com.wf.captcha.base.Captcha; import com.wf.captcha.base.Captcha;
import com.wf.captcha.GifCaptcha;
import com.wf.captcha.SpecCaptcha; import com.wf.captcha.SpecCaptcha;
/** /**
@ -16,21 +15,9 @@ import com.wf.captcha.SpecCaptcha;
*/ */
public class CaptchaUtil { public class CaptchaUtil {
private static final String SESSION_KEY = "captcha"; private static final String SESSION_KEY = "captcha";
private static final int DEFAULT_LEN = 4; // 默认长度
/** private static final int DEFAULT_WIDTH = 130; // 默认宽度
* 验证验证码 private static final int DEFAULT_HEIGHT = 48; // 默认高度
*
* @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;
}
/** /**
* 输出验证码 * 输出验证码
@ -41,7 +28,7 @@ public class CaptchaUtil {
*/ */
public static void out(HttpServletRequest request, HttpServletResponse response) public static void out(HttpServletRequest request, HttpServletResponse response)
throws IOException { 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) public static void out(int len, HttpServletRequest request, HttpServletResponse response)
throws IOException { throws IOException {
out(130, 48, len, request, response); out(DEFAULT_WIDTH, DEFAULT_HEIGHT, 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);
} }
/** /**
@ -89,42 +62,14 @@ public class CaptchaUtil {
/** /**
* 输出验证码 * 输出验证码
* *
* @param width 宽度
* @param height 高度
* @param len 长度
* @param font 字体 * @param font 字体
* @param request HttpServletRequest * @param request HttpServletRequest
* @param response HttpServletResponse * @param response HttpServletResponse
* @throws IOException IO异常 * @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 { throws IOException {
outCaptcha(width, height, len, font, 1, request, response); out(DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_LEN, font, 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);
} }
/** /**
@ -136,24 +81,9 @@ public class CaptchaUtil {
* @param response HttpServletResponse * @param response HttpServletResponse
* @throws IOException IO异常 * @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 { throws IOException {
outPng(130, 48, len, font, request, response); out(DEFAULT_WIDTH, DEFAULT_HEIGHT, 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);
} }
/** /**
@ -167,39 +97,46 @@ public class CaptchaUtil {
* @param response HttpServletResponse * @param response HttpServletResponse
* @throws IOException IO异常 * @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 { 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 captcha Captcha
* @param height 高度
* @param len 长度
* @param font 字体
* @param cType 类型
* @param request HttpServletRequest * @param request HttpServletRequest
* @param response HttpServletResponse * @param response HttpServletResponse
* @throws IOException IO异常 * @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 { throws IOException {
setHeader(response); 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()); request.getSession().setAttribute(SESSION_KEY, captcha.text().toLowerCase());
captcha.out(response.getOutputStream()); 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中的验证码 * 清除session中的验证码
* *

View File

@ -1,15 +1,9 @@
package com.wf.captcha; package com.wf.captcha;
import com.wf.captcha.base.Captcha;
import org.junit.Test; 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.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException;
/** /**
* 测试类 * 测试类
@ -19,98 +13,59 @@ public class CaptchaTest {
@Test @Test
public void test() throws Exception { public void test() throws Exception {
for (int i = 0; i < 5; i++) { /*for (int i = 0; i < 10; i++) {
SpecCaptcha specCaptcha = new SpecCaptcha(); SpecCaptcha specCaptcha = new SpecCaptcha();
// specCaptcha.setCharType(Captcha.TYPE_ONLY_UPPER); specCaptcha.setLen(4);
specCaptcha.setFont(i, 32f);
System.out.println(specCaptcha.text()); 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("C:/Java/aa" + i + ".png")));
specCaptcha.out(new FileOutputStream(new File("D:/Java/aa" + i + ".png"))); }*/
}
} }
@Test @Test
public void testGIf() throws Exception { public void testGIf() throws Exception {
/*for (int i = 0; i < 5; i++) { /*for (int i = 0; i < 10; i++) {
GifCaptcha gifCaptcha = new GifCaptcha(); GifCaptcha gifCaptcha = new GifCaptcha();
gifCaptcha.setLen(5);
gifCaptcha.setFont(i, 32f);
System.out.println(gifCaptcha.text()); 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 @Test
public void testHan() throws Exception { public void testHan() throws Exception {
/*for (int i = 0; i < 5; i++) { /*for (int i = 0; i < 10; i++) {
ChineseCaptcha chineseCaptcha = new ChineseCaptcha(); ChineseCaptcha chineseCaptcha = new ChineseCaptcha();
System.out.println(chineseCaptcha.text()); 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 @Test
public void testGifHan() throws Exception { public void testGifHan() throws Exception {
/*for (int i = 0; i < 5; i++) { /*for (int i = 0; i < 10; i++) {
ChineseGifCaptcha chineseGifCaptcha = new ChineseGifCaptcha(); ChineseGifCaptcha chineseGifCaptcha = new ChineseGifCaptcha();
System.out.println(chineseGifCaptcha.text()); 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 @Test
public void test2() throws Exception { public void testArit() throws Exception {
String[] colors = new String[]{"#8cc540", "#009f5d", "#019fa0", "#019fde", "#007cdc", "#887ddd", "#cd7bdd", "#ff5675", "#ff1244", "#ff8345", "#f8bd0b", "#d1d2d4"}; /*for (int i = 0; i < 10; i++) {
String[] fonts = new String[]{"actionj.ttf", "epilog.ttf", "fresnel.ttf", "headache.ttf", "lexo.ttf", "prefix.ttf", "progbot.ttf", "ransom.ttf", "robot.ttf", "scandal.ttf"}; ArithmeticCaptcha specCaptcha = new ArithmeticCaptcha();
int i = 0; specCaptcha.setLen(3);
for (String f : fonts) { specCaptcha.setFont(i, 28f);
i++; System.out.println(specCaptcha.getArithmeticString() + " " + specCaptcha.text());
FileOutputStream out = new FileOutputStream(new File("D:/Java/aa" + i + ".png")); specCaptcha.out(new FileOutputStream(new File("C:/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();
}
}
}
} }
@Test @Test
public void test3() throws Exception { public void testBase64() throws Exception {
String filePath = getClass().getResource("/actionj.ttf").getFile(); /*GifCaptcha specCaptcha = new GifCaptcha();
System.out.println(filePath); System.out.println(specCaptcha.toBase64(""));*/
} }
} }