增加内置字体、贝塞尔曲线、算术验证码、base64输出
This commit is contained in:
		
							
								
								
									
										402
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										402
									
								
								README.md
									
									
									
									
									
								
							@@ -5,48 +5,54 @@
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## 1.简介
 | 
			
		||||
 Java图形验证码,支持gif、中文、算术等类型,可用于Java Web、JavaSE等项目。
 | 
			
		||||
 | 
			
		||||
  Java图形验证码,支持gif验证码、中文验证码,可用于Java Web、JavaSE项目。
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
## 2.效果展示
 | 
			
		||||
 | 
			
		||||
**gif效果:**
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
 
 | 
			
		||||
  
 | 
			
		||||

 | 
			
		||||

 | 
			
		||||
  
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
**png效果:**
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||

 | 
			
		||||
<br/>
 | 
			
		||||
 
 | 
			
		||||
  
 | 
			
		||||

 | 
			
		||||

 | 
			
		||||
  
 | 
			
		||||

 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
**算术类型:**
 | 
			
		||||
 | 
			
		||||
**中文验证码:**
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||

 | 
			
		||||
  
 | 
			
		||||

 | 
			
		||||

 | 
			
		||||
  
 | 
			
		||||

 | 
			
		||||

 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
**中文类型:**
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
  
 | 
			
		||||

 | 
			
		||||

 | 
			
		||||
  
 | 
			
		||||

 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
**内置字体:**
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
  
 | 
			
		||||

 | 
			
		||||
  
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
## 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
 | 
			
		||||
<project>
 | 
			
		||||
@@ -77,33 +83,49 @@ dependencies {
 | 
			
		||||
       <dependency>
 | 
			
		||||
          <groupId>com.github.whvcse</groupId>
 | 
			
		||||
          <artifactId>EasyCaptcha</artifactId>
 | 
			
		||||
          <version>1.5.0</version>
 | 
			
		||||
          <version>1.6.0</version>
 | 
			
		||||
       </dependency>
 | 
			
		||||
    </dependencies>
 | 
			
		||||
</project>
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### 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添加如下:
 | 
			
		||||
```
 | 
			
		||||
<dependency>
 | 
			
		||||
  <groupId>com.github.whvcse</groupId>
 | 
			
		||||
  <artifactId>EasyCaptcha</artifactId>
 | 
			
		||||
  <version>1.5.0</version>
 | 
			
		||||
  <systemPath>${basedir}/libs/EasyCaptcha-1.5.0.jar</systemPath>
 | 
			
		||||
  <version>1.6.0</version>
 | 
			
		||||
  <systemPath>${basedir}/libs/EasyCaptcha-1.6.0.jar</systemPath>
 | 
			
		||||
</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.快速使用
 | 
			
		||||
1.在web.xml里面加入如下配置:
 | 
			
		||||
> 不要忘了把`/captcha`路径排除登录拦截,比如shiro的拦截。
 | 
			
		||||
 | 
			
		||||
### 4.2.在servlet中使用
 | 
			
		||||
web.xml中配置servlet:
 | 
			
		||||
```xml
 | 
			
		||||
<web-app>
 | 
			
		||||
    <!-- 图形验证码servlet -->
 | 
			
		||||
@@ -113,163 +135,130 @@ maven导入jar包,在项目根目录创建`libs`文件夹,然后pom.xml添
 | 
			
		||||
    </servlet>
 | 
			
		||||
    <servlet-mapping>
 | 
			
		||||
        <servlet-name>CaptchaServlet</servlet-name>
 | 
			
		||||
        <url-pattern>/images/captcha</url-pattern>
 | 
			
		||||
        <url-pattern>/captcha</url-pattern>
 | 
			
		||||
    </servlet-mapping>
 | 
			
		||||
</web-app>
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
2.前端代码
 | 
			
		||||
前端html代码:
 | 
			
		||||
```html
 | 
			
		||||
<img src="/images/captcha" />
 | 
			
		||||
<img src="/captcha" width="130px" height="48px" />
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### 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
 | 
			
		||||
<img src="/images/captcha" />
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### 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验证码
 | 
			
		||||
> 注意:<br/>
 | 
			
		||||
>  算术验证码的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 |  
 | 
			
		||||
 Captcha.FONT_2 | 
 | 
			
		||||
 Captcha.FONT_3 |  
 | 
			
		||||
 Captcha.FONT_4 | 
 | 
			
		||||
 Captcha.FONT_5 | 
 | 
			
		||||
 Captcha.FONT_6 | 
 | 
			
		||||
 Captcha.FONT_7 | 
 | 
			
		||||
 Captcha.FONT_8 | 
 | 
			
		||||
 Captcha.FONT_9 | 
 | 
			
		||||
 Captcha.FONT_10 | 
 | 
			
		||||
 | 
			
		||||
使用方法:
 | 
			
		||||
```
 | 
			
		||||
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
 | 
			
		||||
<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.自定义效果
 | 
			
		||||
 | 
			
		||||
 继承`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)。
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								pom.xml
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								pom.xml
									
									
									
									
									
								
							@@ -5,7 +5,7 @@
 | 
			
		||||
    <groupId>org.wf</groupId>
 | 
			
		||||
 | 
			
		||||
    <artifactId>easy-captcha</artifactId>
 | 
			
		||||
    <version>1.5.0-RELEASE</version>
 | 
			
		||||
    <version>1.6.0-RELEASE</version>
 | 
			
		||||
    <packaging>jar</packaging>
 | 
			
		||||
 | 
			
		||||
    <name>EasyCaptcha</name>
 | 
			
		||||
@@ -23,7 +23,7 @@
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>junit</groupId>
 | 
			
		||||
            <artifactId>junit</artifactId>
 | 
			
		||||
            <version>4.7</version>
 | 
			
		||||
            <version>4.12</version>
 | 
			
		||||
            <scope>test</scope>
 | 
			
		||||
        </dependency>
 | 
			
		||||
    </dependencies>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										96
									
								
								src/main/java/com/wf/captcha/ArithmeticCaptcha.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								src/main/java/com/wf/captcha/ArithmeticCaptcha.java
									
									
									
									
									
										Normal 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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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 {
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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中的验证码
 | 
			
		||||
     *
 | 
			
		||||
 
 | 
			
		||||
@@ -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(""));*/
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user