优化验证码的效果和干扰

This commit is contained in:
synchronized 2018-07-27 15:11:34 +08:00
parent a548f7af0b
commit 67409112a7
8 changed files with 204 additions and 128 deletions

158
pom.xml
View File

@ -20,12 +20,6 @@
</license> </license>
</licenses> </licenses>
<parent>
<groupId>org.sonatype.oss</groupId>
<artifactId>oss-parent</artifactId>
<version>7</version>
</parent>
<dependencies> <dependencies>
<!-- j2ee环境 --> <!-- j2ee环境 -->
<dependency> <dependency>
@ -50,88 +44,86 @@
<configuration> <configuration>
<source>6</source> <source>6</source>
<target>6</target> <target>6</target>
<encoding>UTF-8</encoding>
</configuration> </configuration>
</plugin> </plugin>
</plugins> </plugins>
</build> </build>
<!--
<scm>
<url>https://github.com/whvcse/EasyCaptcha</url>
<connection>https://github.com/whvcse/EasyCaptcha.git</connection>
<developerConnection>https://github.com/whvcse/EasyCaptcha</developerConnection>
</scm>
<scm> <developers>
<url>https://github.com/whvcse/EasyCaptcha</url> <developer>
<connection>scm:git:git@github.com:whvcse/EasyCaptcha</connection> <name>whvcse</name>
<developerConnection>scm:git:git@github.com:whvcse/EasyCaptcha</developerConnection> <email>whvcse@foxmail.com</email>
<tag>HEAD</tag> <url>https://github.com/whvcse</url>
</scm> </developer>
</developers>
<developers> <distributionManagement>
<developer> <snapshotRepository>
<name>whvcse</name> <id>ossrh</id>
<email>whvcse@foxmail.com</email> <url>https://oss.sonatype.org/content/repositories/snapshots/</url>
<url>https://github.com/whvcse</url> </snapshotRepository>
</developer> <repository>
</developers> <id>ossrh</id>
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
</repository>
</distributionManagement>
<distributionManagement> <profiles>
<snapshotRepository> <profile>
<id>oss</id> <id>release</id>
<name>OSS Snapshots Repository</name> <build>
<url>https://oss.sonatype.org/content/repositories/snapshots/</url> <plugins>
</snapshotRepository> &lt;!&ndash; Source &ndash;&gt;
<repository> <plugin>
<id>oss</id> <groupId>org.apache.maven.plugins</groupId>
<name>OSS Staging Repository</name> <artifactId>maven-source-plugin</artifactId>
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url> <version>2.2.1</version>
</repository> <executions>
</distributionManagement> <execution>
<id>attach-sources</id>
<profiles> <goals>
<profile> <goal>jar-no-fork</goal>
<id>release</id> </goals>
<build> </execution>
<plugins> </executions>
<!-- Source --> </plugin>
<plugin> &lt;!&ndash; Javadoc &ndash;&gt;
<groupId>org.apache.maven.plugins</groupId> <plugin>
<artifactId>maven-source-plugin</artifactId> <groupId>org.apache.maven.plugins</groupId>
<version>2.2.1</version> <artifactId>maven-javadoc-plugin</artifactId>
<executions> <version>2.9.1</version>
<execution> <executions>
<phase>package</phase> <execution>
<goals> <id>attach-javadocs</id>
<goal>jar-no-fork</goal> <goals>
</goals> <goal>jar</goal>
</execution> </goals>
</executions> </execution>
</plugin> </executions>
<!-- Javadoc --> </plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId> <artifactId>maven-gpg-plugin</artifactId>
<version>2.9.1</version> <version>1.5</version>
<executions> <executions>
<execution> <execution>
<phase>package</phase> <id>sign-artifacts</id>
<goals> <phase>verify</phase>
<goal>jar</goal> <goals>
</goals> <goal>sign</goal>
</execution> </goals>
</executions> </execution>
</plugin> </executions>
<plugin> </plugin>
<groupId>org.apache.maven.plugins</groupId> </plugins>
<artifactId>maven-gpg-plugin</artifactId> </build>
<version>1.5</version> </profile>
<executions> </profiles>-->
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project> </project>

View File

@ -9,10 +9,10 @@ import java.io.OutputStream;
* Created by 王帆 on 2018-07-27 上午 10:08. * Created by 王帆 on 2018-07-27 上午 10:08.
*/ */
public abstract class Captcha extends Randoms { public abstract class Captcha extends Randoms {
protected Font font = new Font("Verdana", Font.ITALIC | Font.BOLD, 28); // 字体 protected Font font = new Font("Verdana", Font.PLAIN, 32); // 字体
protected int len = 5; // 验证码随机字符长度 protected int len = 5; // 验证码随机字符长度
protected int width = 150; // 验证码显示宽度 protected int width = 130; // 验证码显示宽度
protected int height = 40; // 验证码显示高度 protected int height = 48; // 验证码显示高度
private String chars = null; // 当前验证码 private String chars = null; // 当前验证码
/** /**

View File

@ -97,6 +97,13 @@ public class Encoder {
byte[] accum = new byte[256]; byte[] accum = new byte[256];
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
/**
* @param width 宽度
* @param height 高度
* @param pixels 像素
* @param color_depth 颜色
*/
Encoder(int width, int height, byte[] pixels, int color_depth) { Encoder(int width, int height, byte[] pixels, int color_depth) {
imgW = width; imgW = width;
imgH = height; imgH = height;
@ -106,6 +113,12 @@ public class Encoder {
// Add a character to the end of the current packet, and if it is 254 // Add a character to the end of the current packet, and if it is 254
// characters, flush the packet to disk. // characters, flush the packet to disk.
/**
* @param c 字节
* @param outs 输出流
* @throws IOException IO异常
*/
void char_out(byte c, OutputStream outs) throws IOException { void char_out(byte c, OutputStream outs) throws IOException {
accum[a_count++] = c; accum[a_count++] = c;
if (a_count >= 254) if (a_count >= 254)
@ -115,6 +128,11 @@ public class Encoder {
// Clear out the hash table // Clear out the hash table
// table clear for block compress // table clear for block compress
/**
* @param outs 输出流
* @throws IOException IO异常
*/
void cl_block(OutputStream outs) throws IOException { void cl_block(OutputStream outs) throws IOException {
cl_hash(hsize); cl_hash(hsize);
free_ent = ClearCode + 2; free_ent = ClearCode + 2;
@ -124,11 +142,20 @@ public class Encoder {
} }
// reset code table // reset code table
/**
* @param hsize int
*/
void cl_hash(int hsize) { void cl_hash(int hsize) {
for (int i = 0; i < hsize; ++i) for (int i = 0; i < hsize; ++i)
htab[i] = -1; htab[i] = -1;
} }
/**
* @param init_bits int
* @param outs 输出流
* @throws IOException IO异常
*/
void compress(int init_bits, OutputStream outs) throws IOException { void compress(int init_bits, OutputStream outs) throws IOException {
int fcode; int fcode;
int i /* = 0 */; int i /* = 0 */;
@ -201,6 +228,11 @@ public class Encoder {
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
/**
* @param os 输出流
* @throws IOException IO异常
*/
void encode(OutputStream os) throws IOException { void encode(OutputStream os) throws IOException {
os.write(initCodeSize); // write "initial code size" byte os.write(initCodeSize); // write "initial code size" byte
@ -213,6 +245,11 @@ public class Encoder {
} }
// Flush the packet to disk, and reset the accumulator // Flush the packet to disk, and reset the accumulator
/**
* @param outs 输出流
* @throws IOException IO异常
*/
void flush_char(OutputStream outs) throws IOException { void flush_char(OutputStream outs) throws IOException {
if (a_count > 0) { if (a_count > 0) {
outs.write(a_count); outs.write(a_count);
@ -221,6 +258,10 @@ public class Encoder {
} }
} }
/**
* @param n_bits int
* @return int
*/
final int MAXCODE(int n_bits) { final int MAXCODE(int n_bits) {
return (1 << n_bits) - 1; return (1 << n_bits) - 1;
} }
@ -228,6 +269,10 @@ public class Encoder {
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
// Return the next pixel from the image // Return the next pixel from the image
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
/**
* @return int
*/
private int nextPixel() { private int nextPixel() {
if (remaining == 0) if (remaining == 0)
return EOF; return EOF;
@ -239,6 +284,11 @@ public class Encoder {
return pix & 0xff; return pix & 0xff;
} }
/**
* @param code int
* @param outs 输出流
* @throws IOException IO异常
*/
void output(int code, OutputStream outs) throws IOException { void output(int code, OutputStream outs) throws IOException {
cur_accum &= masks[cur_bits]; cur_accum &= masks[cur_bits];

View File

@ -1,9 +1,6 @@
package com.wf.captcha; package com.wf.captcha;
import java.awt.AlphaComposite; import java.awt.*;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
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;
@ -82,12 +79,26 @@ public class GifCaptcha extends Captcha {
int h = height - ((height - font.getSize()) >> 1); int h = height - ((height - font.getSize()) >> 1);
int w = width / len; int w = width / len;
g2d.setFont(font); g2d.setFont(font);
// 随机画干扰线
for (int i = 0; i < 8; i++) {
int x1 = num(-10, width - 10);
int y1 = num(5, height - 5);
int x2 = num(10, width + 10);
int y2 = num(2, height - 2);
g2d.setColor(color(150, 250));
g2d.setStroke(new BasicStroke(1.3f));
g2d.drawLine(x1, y1, x2, y2);
// 画干扰圆圈
g2d.setColor(color(100, 250));
g2d.setStroke(new BasicStroke(1.0f));
g2d.drawOval(num(width), num(height), 5 + num(10), 5 + num(10));
}
// 画验证码
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
ac3 = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, getAlpha(flag, i)); ac3 = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, getAlpha(flag, i));
g2d.setComposite(ac3); g2d.setComposite(ac3);
g2d.setColor(fontcolor[i]); g2d.setColor(fontcolor[i]);
g2d.drawOval(num(width), num(height), 5 + num(10), 5 + num(10)); g2d.drawString(String.valueOf(strs[i]), (width - (len - i) * w) + (w - font.getSize()) + num(7, 11), h - num(2, 6));
g2d.drawString(String.valueOf(strs[i]), (width - (len - i) * w) + (w - font.getSize()) + 1, h - 4);
} }
g2d.dispose(); g2d.dispose();
return image; return image;

View File

@ -79,7 +79,6 @@ public class GifEncoder {
* image is added. * image is added.
* *
* @param iter int number of iterations. * @param iter int number of iterations.
* @return
*/ */
public void setRepeat(int iter) { public void setRepeat(int iter) {
if (iter >= 0) { if (iter >= 0) {
@ -168,6 +167,8 @@ public class GifEncoder {
* Flushes any pending data and closes output file. * Flushes any pending data and closes output file.
* If writing to an OutputStream, the stream is not * If writing to an OutputStream, the stream is not
* closed. * closed.
*
* @return boolean
*/ */
public boolean finish() { public boolean finish() {
if (!started) return false; if (!started) return false;
@ -219,7 +220,6 @@ public class GifEncoder {
* than 20 do not yield significant improvements in speed. * than 20 do not yield significant improvements in speed.
* *
* @param quality int greater than 0. * @param quality int greater than 0.
* @return
*/ */
public void setQuality(int quality) { public void setQuality(int quality) {
if (quality < 1) quality = 1; if (quality < 1) quality = 1;
@ -319,6 +319,9 @@ public class GifEncoder {
/** /**
* Returns index of palette color closest to c * Returns index of palette color closest to c
*
* @param c color
* @return int
*/ */
protected int findClosest(Color c) { protected int findClosest(Color c) {
if (colorTab == null) return -1; if (colorTab == null) return -1;
@ -365,6 +368,8 @@ public class GifEncoder {
/** /**
* Writes Graphic Control Extension * Writes Graphic Control Extension
*
* @throws IOException IO异常
*/ */
protected void writeGraphicCtrlExt() throws IOException { protected void writeGraphicCtrlExt() throws IOException {
out.write(0x21); // extension introducer out.write(0x21); // extension introducer
@ -396,6 +401,8 @@ public class GifEncoder {
/** /**
* Writes Image Descriptor * Writes Image Descriptor
*
* @throws IOException IO异常
*/ */
protected void writeImageDesc() throws IOException { protected void writeImageDesc() throws IOException {
out.write(0x2c); // image separator out.write(0x2c); // image separator
@ -419,6 +426,8 @@ public class GifEncoder {
/** /**
* Writes Logical Screen Descriptor * Writes Logical Screen Descriptor
*
* @throws IOException IO异常
*/ */
protected void writeLSD() throws IOException { protected void writeLSD() throws IOException {
// logical screen size // logical screen size
@ -437,6 +446,8 @@ public class GifEncoder {
/** /**
* Writes Netscape application extension to define * Writes Netscape application extension to define
* repeat count. * repeat count.
*
* @throws IOException IO异常
*/ */
protected void writeNetscapeExt() throws IOException { protected void writeNetscapeExt() throws IOException {
out.write(0x21); // extension introducer out.write(0x21); // extension introducer
@ -451,6 +462,8 @@ public class GifEncoder {
/** /**
* Writes color table * Writes color table
*
* @throws IOException IO异常
*/ */
protected void writePalette() throws IOException { protected void writePalette() throws IOException {
out.write(colorTab, 0, colorTab.length); out.write(colorTab, 0, colorTab.length);
@ -462,6 +475,8 @@ public class GifEncoder {
/** /**
* Encodes and writes pixel data * Encodes and writes pixel data
*
* @throws IOException IO异常
*/ */
protected void writePixels() throws IOException { protected void writePixels() throws IOException {
Encoder encoder = new Encoder(width, height, indexedPixels, colorDepth); Encoder encoder = new Encoder(width, height, indexedPixels, colorDepth);
@ -470,6 +485,9 @@ public class GifEncoder {
/** /**
* Write 16-bit value to output stream, LSB first * Write 16-bit value to output stream, LSB first
*
* @param value int
* @throws IOException IO异常
*/ */
protected void writeShort(int value) throws IOException { protected void writeShort(int value) throws IOException {
out.write(value & 0xff); out.write(value & 0xff);
@ -478,6 +496,9 @@ public class GifEncoder {
/** /**
* Writes string to output stream * Writes string to output stream
*
* @param s string
* @throws IOException IO异常
*/ */
protected void writeString(String s) throws IOException { protected void writeString(String s) throws IOException {
for (int i = 0; i < s.length(); i++) { for (int i = 0; i < s.length(); i++) {

View File

@ -1,9 +1,6 @@
package com.wf.captcha; package com.wf.captcha;
import java.awt.AlphaComposite; import java.awt.*;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
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;
@ -59,30 +56,31 @@ public class SpecCaptcha extends Captcha {
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 g = (Graphics2D) bi.getGraphics();
AlphaComposite ac3; AlphaComposite ac3;
Color color;
int len = strs.length; int len = strs.length;
g.setColor(Color.WHITE); g.setColor(Color.WHITE);
g.fillRect(0, 0, width, height); g.fillRect(0, 0, width, height);
// 随机画干扰的圆圈 // 随机画干扰线
for (int i = 0; i < 15; i++) { for (int i = 0; i < 12; i++) {
color = color(150, 250); int x1 = num(-10, width - 10);
g.setColor(color); int y1 = num(5, height - 5);
g.drawOval(num(width), num(height), 5 + num(10), 5 + num(10)); int x2 = num(10, width + 10);
color = null; int y2 = num(2, height - 2);
g.setColor(color(150, 250));
g.setStroke(new BasicStroke(1.3f));
g.drawLine(x1, y1, x2, y2);
// 画干扰圆圈
g.setColor(color(100, 250));
g.drawOval(num(width), num(height), 5 + num(25), 5 + num(25));
} }
g.setFont(font); g.setFont(new Font(font.getFontName(), Font.ITALIC, font.getSize()));
int h = height - ((height - font.getSize()) >> 1); int h = height - ((height - font.getSize()) >> 1);
int w = width / len; int w = width / len;
int size = w - font.getSize() + 1;
// 画字符串 // 画字符串
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
ac3 = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.7f);// 指定透明度 ac3 = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.75f);// 指定透明度
g.setComposite(ac3); g.setComposite(ac3);
color = new Color(20 + num(110), 20 + num(110), 20 + num(110));// 对每个字符都用随机颜色 g.setColor(new Color(20 + num(110), 20 + num(110), 20 + num(110)));
g.setColor(color); g.drawString(String.valueOf(strs[i]), (width - (len - i) * w) + (w - font.getSize()) + num(7, 11), h - num(2, 6));
g.drawString(String.valueOf(strs[i]), (width - (len - i) * w) + size, h - 4);
color = null;
ac3 = null;
} }
ImageIO.write(bi, "png", out); ImageIO.write(bi, "png", out);
out.flush(); out.flush();

View File

@ -17,6 +17,10 @@ public class CaptchaUtil {
/** /**
* 验证验证码 * 验证验证码
*
* @param code 用户输入的验证码
* @param request HttpServletRequest
* @return 是否正确
*/ */
public static boolean ver(String code, HttpServletRequest request) { public static boolean ver(String code, HttpServletRequest request) {
if (code != null && !code.trim().isEmpty()) { if (code != null && !code.trim().isEmpty()) {
@ -29,26 +33,26 @@ public class CaptchaUtil {
/** /**
* 输出验证码 * 输出验证码
* *
* @param request * @param request HttpServletRequest
* @param response * @param response HttpServletResponse
* @throws IOException * @throws IOException IO异常
*/ */
public static void out(HttpServletRequest request, HttpServletResponse response) public static void out(HttpServletRequest request, HttpServletResponse response)
throws IOException { throws IOException {
out(130, 38, 5, request, response); out(5, request, response);
} }
/** /**
* 输出验证码 * 输出验证码
* *
* @param len 长度 * @param len 长度
* @param request * @param request HttpServletRequest
* @param response * @param response HttpServletResponse
* @throws IOException * @throws IOException IO异常
*/ */
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, 38, len, request, response); out(130, 48, len, request, response);
} }
/** /**
@ -57,14 +61,14 @@ public class CaptchaUtil {
* @param width 宽度 * @param width 宽度
* @param height 高度 * @param height 高度
* @param len 长度 * @param len 长度
* @param request * @param request HttpServletRequest
* @param response * @param response HttpServletResponse
* @throws IOException * @throws IOException IO异常
*/ */
public static void out(int width, int height, int len, HttpServletRequest request, HttpServletResponse response) public static void out(int width, int height, int len, HttpServletRequest request, HttpServletResponse response)
throws IOException { throws IOException {
setHeader(response); setHeader(response);
Captcha captcha = new GifCaptcha(130, 38, 5); Captcha captcha = new GifCaptcha(width, height, len);
request.getSession().setAttribute(SESSION_KEY, captcha.text().toLowerCase()); request.getSession().setAttribute(SESSION_KEY, captcha.text().toLowerCase());
captcha.out(response.getOutputStream()); captcha.out(response.getOutputStream());
} }
@ -72,7 +76,7 @@ public class CaptchaUtil {
/** /**
* 设置相应头 * 设置相应头
* *
* @param response * @param response HttpServletResponse
*/ */
private static void setHeader(HttpServletResponse response) { private static void setHeader(HttpServletResponse response) {
response.setContentType("image/gif"); response.setContentType("image/gif");

View File

@ -13,14 +13,14 @@ public class CaptchaTest {
@Test @Test
public void test() throws Exception { public void test() throws Exception {
SpecCaptcha specCaptcha = new SpecCaptcha(150, 40, 4); SpecCaptcha specCaptcha = new SpecCaptcha();
System.out.println(specCaptcha.text()); System.out.println(specCaptcha.text());
specCaptcha.out(new FileOutputStream(new File("D:/a/aa.png"))); specCaptcha.out(new FileOutputStream(new File("D:/a/aa.png")));
} }
@Test @Test
public void testGIf() throws Exception { public void testGIf() throws Exception {
GifCaptcha specCaptcha = new GifCaptcha(150, 40, 4); GifCaptcha specCaptcha = new GifCaptcha(130, 48, 5);
System.out.println(specCaptcha.text()); System.out.println(specCaptcha.text());
specCaptcha.out(new FileOutputStream(new File("D:/a/aa.gif"))); specCaptcha.out(new FileOutputStream(new File("D:/a/aa.gif")));
} }