一、验证码
验证码(CAPTCHA)是“Completely Automated Public Turing test to tell Computers and Humans Apart”(全自动区分计算机和人类的图灵测试)的缩写,是一种区分用户是计算机还是人的公共全自动程序。可以防止:恶意破解密码、刷票、论坛灌水,有效防止某个黑客对某一个特定注册用户用特定程序暴力破解方式进行不断的登陆尝试,实际上用验证码是现在很多网站通行的方式,我们利用比较简易的方式实现了这个功能。这个问题可以由计算机生成并评判,但是必须只有人类才能解答。由于计算机无法解答CAPTCHA的问题,所以回答出问题的用户就可以被认为是人类。
二、Servlet图片验证码实现
图片验证的实现,我们可以直接将生成图片的代码和文字资源结合在一起,通过后台去读取文字资源中的文字,将资源文件放入/WEB-INF/new_words.txt
平易近人
宽宏大度
冰清玉洁
持之以恒
锲而不舍
废寝忘食
大义凛然
临危不俱
光明磊落
不屈不挠
鞠躬尽瘁
死而后已
代码:
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 验证码生成程序
*/
public class CheckWord extends HttpServlet {
// 集合中保存所有成语
private List<String> words = new ArrayList<String>();
@Override
public void init() throws ServletException {
// 初始化阶段,读取new_words.txt
// web工程中读取 文件,必须使用绝对磁盘路径
String path = getServletContext().getRealPath("/WEB-INF/new_words.txt");
try {
BufferedReader reader = new BufferedReader(new FileReader(path));
String line;
while ((line = reader.readLine()) != null) {
words.add(line);
}
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 禁止缓存
// response.setHeader("Cache-Control", "no-cache");
// response.setHeader("Pragma", "no-cache");
// response.setDateHeader("Expires", -1);
int width = 120;
int height = 30;
// 步骤一 绘制一张内存中图片
BufferedImage bufferedImage = new BufferedImage(width, height,
BufferedImage.TYPE_INT_RGB);
// 步骤二 图片绘制背景颜色 ---通过绘图对象
Graphics graphics = bufferedImage.getGraphics();// 得到画图对象 --- 画笔
// 绘制任何图形之前 都必须指定一个颜色
graphics.setColor(getRandColor(200, 250));
graphics.fillRect(0, 0, width, height);
// 步骤三 绘制边框
graphics.setColor(Color.WHITE);
graphics.drawRect(0, 0, width - 1, height - 1);
// 步骤四 四个随机数字
Graphics2D graphics2d = (Graphics2D) graphics;
// 设置输出字体
graphics2d.setFont(new Font("宋体", Font.BOLD, 18));
Random random = new Random();// 生成随机数
int index = random.nextInt(words.size());
String word = words.get(index);// 获得成语
// 定义x坐标
int x = 10;
for (int i = 0; i < word.length(); i++) {
// 随机颜色
graphics2d.setColor(new Color(20 + random.nextInt(110), 20 + random
.nextInt(110), 20 + random.nextInt(110)));
// 旋转 -30 --- 30度
int jiaodu = random.nextInt(60) - 30;
// 换算弧度
double theta = jiaodu * Math.PI / 180;
// 获得字母数字
char c = word.charAt(i);
// 将c 输出到图片
graphics2d.rotate(theta, x, 20);
graphics2d.drawString(String.valueOf(c), x, 20);
graphics2d.rotate(-theta, x, 20);
x += 30;
}
// 将验证码内容保存session
request.getSession().setAttribute("checkcode_session", word);
// 步骤五 绘制干扰线
graphics.setColor(getRandColor(160, 200));
int x1;
int x2;
int y1;
int y2;
for (int i = 0; i < 30; i++) {
x1 = random.nextInt(width);
x2 = random.nextInt(12);
y1 = random.nextInt(height);
y2 = random.nextInt(12);
graphics.drawLine(x1, y1, x1 + x2, x2 + y2);
}
// 将上面图片输出到浏览器 ImageIO
graphics.dispose();// 释放资源
//将图片写到response.getOutputStream()中
ImageIO.write(bufferedImage, "jpg", response.getOutputStream());
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
/**
* 取其某一范围的color
*
* @param fc
* int 范围参数1
* @param bc
* int 范围参数2
* @return Color
*/
private Color getRandColor(int fc, int bc) {
// 取其随机颜色
Random random = new Random();
if (fc > 255) {
fc = 255;
}
if (bc > 255) {
bc = 255;
}
int r = fc + random.nextInt(bc - fc);
int g = fc + random.nextInt(bc - fc);
int b = fc + random.nextInt(bc - fc);
return new Color(r, g, b);
}
}
JSP代码:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>登录页面</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<script type="text/javascript">
function checkImg(object){
object.src="/mark/checkWord?time=" + new Date().getTime();
}
</script>
</head>
<body>
<form action="/C3P0Test/UserSQL" method="post">
<div>
用户名:<input type="text" name="username">
</div>
<div>
密码:<input type="password" name="password">
</div>
<div>
验证码:<input type="text" name="checkWord">
<span>
<img alt="" src="/mark/checkWord" onclick="checkImg(this)">
</span>
</div>
<div>
<input type="submit" value="提交">
</div>
</form>
</body>
</html>
三、Kaptcha
Kaptcha是一个非常实用的验证码生成工具,可以通过配置生成多样化的验证码。
Kaptcha 是一个可高度配置的实用验证码生成工具,可自由配置的选项如:
验证码的字体
验证码字体的大小
验证码字体的字体颜色
验证码内容的范围(数字,字母,中文汉字!)
验证码图片的大小,边框,边框粗细,边框颜色
验证码的干扰线
验证码的样式(鱼眼样式、3D、普通模糊、...)
Kaptcha 是一个可高度配置的实用验证码生成工具,可自由配置的选项如:
Kaptcha 详细配置表
Constant | 描述 | 默认值 |
---|---|---|
kaptcha.border | 图片边框,合法值:yes , no | yes |
kaptcha.border.color | 边框颜色,合法值: r,g,b (and optional alpha) 或者 white,black,blue. | black |
kaptcha.image.width | 图片宽 | 200 |
kaptcha.image.height | 图片高 | 50 |
kaptcha.producer.impl | 图片实现类 | com.google.code.kaptcha.impl.DefaultKaptcha |
kaptcha.textproducer.impl | 文本实现类 | com.google.code.kaptcha.text.impl.DefaultTextCreator |
kaptcha.textproducer.char.string | 文本集合,验证码值从此集合中获取 | abcde2345678gfynmnpwx |
kaptcha.textproducer.char.length | 验证码长度 | 5 |
kaptcha.textproducer.font.names | 字体 | Arial, Courier |
kaptcha.textproducer.font.size | 字体大小 | 40px. |
kaptcha.textproducer.font.color | 字体颜色,合法值: r,g,b 或者 white,black,blue. | black |
kaptcha.textproducer.char.space | 文字间隔 | 2 |
kaptcha.noise.impl | 干扰实现类 | com.google.code.kaptcha.impl.DefaultNoise |
kaptcha.noise.color | 干扰 颜色,合法值: r,g,b 或者 white,black,blue. | black |
kaptcha.obscurificator.impl | 图片样式: 水纹 com.google.code.kaptcha.impl.WaterRipple 鱼眼 com.google.code.kaptcha.impl.FishEyeGimpy 阴影 com.google.code.kaptcha.impl.ShadowGimpy |
com.google.code.kaptcha.impl.WaterRipple |
kaptcha.background.impl | 背景实现类 | com.google.code.kaptcha.impl.DefaultBackground |
kaptcha.background.clear.from | 背景颜色渐变,开始颜色 | light grey |
kaptcha.background.clear.to | 背景颜色渐变, 结束颜色 | white |
kaptcha.word.impl | 文字渲染器 | com.google.code.kaptcha.text.impl.DefaultWordRenderer |
kaptcha.session.key | session key | KAPTCHA_SESSION_KEY |
kaptcha.session.date | session date | KAPTCHA_SESSION_DATE |
1、Maven依赖
<dependency>
<groupId>com.github.penggle</groupId>
<artifactId>kaptcha</artifactId>
<version>2.3.2</version>
</dependency>
2、Servlet使用
1)Servlet
@WebServlet(name = "kaptchaServlet",
urlPatterns = "/kaptchaServlet",
initParams = {
//是否有边框
@WebInitParam(name= "kaptcha.border", value="no"),
//字体颜色
@WebInitParam(name= "kaptcha.textproducer.font.color", value= "red"),
//图片宽度
@WebInitParam(name= "kaptcha.image.width", value= "135"),
//使用哪些字符生成验证码
@WebInitParam(name= "kaptcha.textproducer.char.string", value= "ACDEFHKPRSTWX345678"),
//图片高度
@WebInitParam(name= "kaptcha.image.height", value= "50"),
//字体大小
@WebInitParam(name= "kaptcha.textproducer.font.size", value= "43"),
//干扰线的颜色
@WebInitParam(name= "kaptcha.noise.color", value= "black"),
//字符的个数
@WebInitParam(name= "kaptcha.textproducer.char.length", value= "4"),
//字体
@WebInitParam(name= "kaptcha.textproducer.font.names", value= "Arial")
})
public class kaptchaServlet extends com.google.code.kaptcha.servlet.KaptchaServlet {
}
2) HTML
<img id="kaptcha-img" alt="点击更换" title="点击更换"
onclick="changeVerifyCode(this)" src="/kaptchaServlet" />
<script language="JavaScript">
function changeVerifyCode(img) {
img.src="/kaptchaServlet?" + Math.floor(Math.random()*100);
}
</script>
3、在webapp初始化的时候,将Kaptcha加到servletContext中
SpringMVC:
public class WebAppInit implements WebApplicationInitializer {
@Override
public void onStartup(javax.servlet.ServletContext servletContext) throws ServletException {
// Load Spring web application configuration
AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
ac.register(WebMvcConfig.class);
// ac.refresh();此处应该注释,否则报错
// Create and register the DispatcherServlet
DispatcherServlet servlet = new DispatcherServlet(ac);
ServletRegistration.Dynamic registration = servletContext.addServlet("DispatcherServlet", servlet);
registration.setLoadOnStartup(1);
registration.addMapping("/");
//配置生成验证码的kaptchaServlet
//第一步,创建一个kaptchaServlet
KaptchaServlet kaptchaServlet = new KaptchaServlet();
//添加到servletContext
ServletRegistration.Dynamic kaptchaServlet1 = servletContext.addServlet("KaptchaServlet", kaptchaServlet);
//创建一个map用来存放kapchaServlet的初始化参数
HashMap<String, String> map = new HashMap<>();
// <!-- 是否有边框 --> yes or no
map.put("kaptcha.border", "no");
//<!-- 字体颜色 -->
map.put("kaptcha.textproducer.font.color", "red");
// <!-- 图片宽度 -->
map.put("kaptcha.image.width", "135");
// <!-- 使用哪些字符生成验证码 -->
map.put("kaptcha.textproducer.char.string", "ACDEFHKPRSTWX345679");
// <!-- 图片高度 -->
map.put("kaptcha.image.height", "50");
// <!-- 字体大小 -->
map.put("kaptcha.textproducer.font.siz", "43");
// <!-- 干扰线颜色 -->
map.put("kaptcha.noise.color", "black");
// <!-- 验证码字符个数 -->
map.put("kaptcha.textproducer.char.length", "4");
//<!-- 字体 -->
map.put("kaptcha.textproducer.font.names", "Arial");
//设置kapchaServlet的初始化参数
kaptchaServlet1.setInitParameters(map);
//设置
kaptchaServlet1.addMapping("/Kaptcha");
}
}
3.在前端页面用src的属性引入
<img id="captcha_img" alt="点击更换" title="点击更换"
src="/Kaptcha" />
可以用js给这个图片添加一个点击事件,点击之后就更换一个图片
com.google.code.kaptcha.Constants.KAPTCHA_SESSION_KEY
//获取验证码值
(String)servletRequest.getSession().getAttribute("KAPTCHA_SESSION_KEY");
4、SpringBoot使用
1)配置文件
@Component
public class KaptchaConfig {
@Bean
public DefaultKaptcha getDefaultKaptcha() {
com.google.code.kaptcha.impl.DefaultKaptcha defaultKaptcha = new com.google.code.kaptcha.impl.DefaultKaptcha();
Properties properties = new Properties();
// 图片边框
properties.setProperty("kaptcha.border", "yes");
// 边框颜色
properties.setProperty("kaptcha.border.color", "105,179,90");
// 字体颜色
properties.setProperty("kaptcha.textproducer.font.color", "red");
// 图片宽
properties.setProperty("kaptcha.image.width", "110");
// 图片高
properties.setProperty("kaptcha.image.height", "40");
// 字体大小
properties.setProperty("kaptcha.textproducer.font.size", "30");
// session key
properties.setProperty("kaptcha.session.key", "code");
// 验证码长度
properties.setProperty("kaptcha.textproducer.char.length", "4");
// 字体
properties.setProperty("kaptcha.textproducer.font.names", "宋体,楷体,微软雅黑");
Config config = new Config(properties);
defaultKaptcha.setConfig(config);
return defaultKaptcha;
}
}
2)控制器
@Controller
@RequestMapping(value = "/front/member")
public class KaptchaController {
/**
* 1、验证码工具
*/
@Autowired
DefaultKaptcha defaultKaptcha;
/**
* 2、生成验证码
* @param httpServletRequest
* @param httpServletResponse
* @throws Exception
*/
@RequestMapping("/defaultKaptcha")
public void defaultKaptcha(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse)
throws Exception {
byte[] captchaChallengeAsJpeg = null;
ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream();
try {
// 生产验证码字符串并保存到session中
String createText = defaultKaptcha.createText();
httpServletRequest.getSession().setAttribute("verificationCode", createText);
// 使用生产的验证码字符串返回一个BufferedImage对象并转为byte写入到byte数组中
BufferedImage challenge = defaultKaptcha.createImage(createText);
ImageIO.write(challenge, "jpg", jpegOutputStream);
} catch (IllegalArgumentException e) {
httpServletResponse.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
// 定义response输出类型为image/jpeg类型,使用response输出流输出图片的byte数组
captchaChallengeAsJpeg = jpegOutputStream.toByteArray();
httpServletResponse.setHeader("Cache-Control", "no-store");
httpServletResponse.setHeader("Pragma", "no-cache");
httpServletResponse.setDateHeader("Expires", 0);
httpServletResponse.setContentType("image/jpeg");
ServletOutputStream responseOutputStream = httpServletResponse.getOutputStream();
responseOutputStream.write(captchaChallengeAsJpeg);
responseOutputStream.flush();
responseOutputStream.close();
}
/**
* 3、校对验证码
* @param httpServletRequest
* @return
*/
@RequestMapping("/imgvrifyControllerDefaultKaptcha")
public ModelAndView imgvrifyControllerDefaultKaptcha(HttpServletRequest httpServletRequest) {
ModelAndView andView = new ModelAndView();
String rightCode = (String) httpServletRequest.getSession().getAttribute("verificationCode");
String tryCode = httpServletRequest.getParameter("tryCode");
System.out.println("rightCode:"+rightCode+" ———— tryCode:"+tryCode);
if (!rightCode.equals(tryCode)) {
andView.addObject("info", "错误的验证码");
andView.setViewName("index");
} else {
andView.addObject("info", "登录成功");
andView.setViewName("success");
}
return andView;
}
}