概述
验证码也是我们在web应用中经常要用到的功能。基本思路就是,我们在服务端动态的生一成张图片,然后将它输出到客户端。图片上包含一些字符信息,我们将这些字符信息事先保存在session中,那么客户端在看到图片之后,将图片上的字符输出到表单中,然后将表单提交。我们接收到表单数据之后,对表单中提交的验证码与session中保存的验证码进行比对,如果相同,那么验证通过。否则,验证失败!采取一些处理。
验证码的主要作用就是用在用户登录上,能有效的防止客户端多次发送登录请求来暴力破解。由于验证码信息是以图片的形式呈现的,因此要想通过程序来识别这些字符还是不太容易的。当然验证码也不是绝对安全的,但是相对来说它的实现比较容易,安全性也相对较高,所以使用的非常广泛。
使用jsp/servlet实现图片验证码
这里我们主要是学习验证码的实现原理,因此就不做的过于复杂了。其实有很多开源的验证码生成库,他们做的比较精密,集成也比较方便,因此有需要的话可以使用第三方的库。为了生成验证码的部分代码能够被重用,我将它单独提取成为一个类。
public class AuthCode {
private ByteArrayInputStream input;
private ByteArrayOutputStream output;
private String code;// 验证码
private int codeNum;// 验证码字符数量
private int width;
private int height;
// 构造器
private AuthCode(int width, int height, int codeNum) {
this.width = width;
this.height = height;
this.codeNum = codeNum;
if (width < 15 * codeNum + 6) {
this.width = 13 * codeNum + 6;
}
if (height < 20) {
this.height = 20;
}
buildImage();
}
// 以字符串形式返回验证码
public String getCode() {
return code;
}
// 以输入流的形式返回验证图片
public ByteArrayInputStream getIamgeAsInputStream() {
return input;
}
// 以输出流的形式返回验证图片
public ByteArrayOutputStream getImageAsOuputStream() {
return output;
}
// 创建默认大小的验证码
public static AuthCode createInstance() {
return new AuthCode(85, 20, 4);
}
// 创建指定大小的验证码
public static AuthCode createInstance(int width, int height, int codeNum) {
return new AuthCode(width, height, codeNum);
}
// 生成验证码图片
private void buildImage() {
BufferedImage image = new BufferedImage(width, height,
BufferedImage.TYPE_INT_RGB);
// 获取图形上下文
Graphics g = image.getGraphics();
// 生成随机类
Random random = new Random();
// 设定背景色
g.setColor(getRandColor(200, 250));
g.fillRect(0, 0, width, height);
// 设定字体
g.setFont(new Font("Times New Roman", Font.PLAIN, 18));
// 随机产生150条干扰线,使图象中的认证码不易被其它程序探测到
g.setColor(getRandColor(160, 200));
for (int i = 0; i < 150; i++) {
int x = random.nextInt(width);
int y = random.nextInt(height);
int xl = random.nextInt(12);
int yl = random.nextInt(12);
g.drawLine(x, y, x + xl, y + yl);
}
// 取随机产生的认证码
String codes = "ABCDEFGHJKLMNOPQRSTUVWXYZ23456789";
String sRand = "";
for (int i = 0; i < codeNum; i++) {
String rand = codes.charAt(random.nextInt(codes.length())) + "";
sRand += rand;
// 将认证码显示到图象中
g.setColor(new Color(20 + random.nextInt(110), 20 + random
.nextInt(110), 20 + random.nextInt(110)));
// 将字符串绘制到图片上
g.drawString(rand, i * (width / codeNum) + 6, (int)((height+12)/2));
}
/* 验证码赋值 */
this.code = sRand;
// 图象生效
g.dispose();
try {
output = new ByteArrayOutputStream();
ImageOutputStream imageOut = ImageIO
.createImageOutputStream(output);
ImageIO.write(image, "JPEG", imageOut);
imageOut.close();
input = new ByteArrayInputStream(output.toByteArray());
} catch (Exception e) {
System.out.println("验证码图片产生出现错误:" + e.toString());
}
}
// 获取随机颜色
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);
}
}
然后我们定义一个Servlet专门用于输出验证码:
public class AuthCodeServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
AuthCode code = AuthCode.createInstance();
req.getSession().setAttribute("authCode", code.getCode());
resp.getOutputStream()
.write(code.getImageAsOuputStream().toByteArray());
}
}
index.jsp
<html>
<head>
<title>Auth Code</title>
<script type="text/javascript">
function changeImage(obj){
obj.src = "AuthCodeServlet?="+Math.random();
}
</script>
</head>
<body>
<form action="LoginServlet" method="post">
username:
<input type="text" name="userName" />
<br>
password:
<input type="password" name="password" />
<br />
authcode:
<input type="text" name="authCode" />
<img src="AuthCodeServlet" onclick="changeImage(this)" />
<br>
<input type="submit" value="submit" />
</form>
</body>
</html>
LoginServlet.java
public class LoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String userName = req.getParameter("userName");
String password = req.getParameter("password");
String authCode = req.getParameter("authCode");
String code = (String) req.getSession().getAttribute("authCode");
if (authCode != null && authCode.toUpperCase().equals(code)
&& "hello".equals(userName) && "world".equals(password)) {
resp.getWriter().println("login success! authCode:"+authCode);
}else{
resp.getWriter().println("login failed! authCode:"+authCode);
}
}
}
测试:
使用Struts2实现图片验证码
Struts2中有一种名为”stream”类型的Result,我们在学习文件下载的时候已经学习过了。我们已经知道只要我们给它提供一个输入流,只需要设置好相应的ContentType,那么它就会自动将这个输入流输出到浏览器中。那么在这里我们依然可以利用它的这个特性。在上一个例子中的验证码生成类中,提供了一个获取输入流的方法,因此这里我还是使用上面的验证码生成类AuthCode。
AuthCodeAction.java
public class AuthCodeAction extends ActionSupport implements SessionAware {
private Map<String, Object> session;
public void setSession(Map<String, Object> session) {
this.session = session;
}
public InputStream getInputStream() {
AuthCode code = AuthCode.createInstance();
session.put("authCode", code.getCode());
return code.getIamgeAsInputStream();
}
@Override
public String execute() throws Exception {
return SUCCESS;
}
}
LoginAction.java
public class LoginAction extends ActionSupport implements SessionAware {
private String userName;
private String password;
private String authCode;
private Map<String, Object> session;
public void setSession(Map<String, Object> session) {
this.session = session;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getAuthCode() {
return authCode;
}
public void setAuthCode(String authCode) {
this.authCode = authCode;
}
public Map<String, Object> getSession() {
return session;
}
@Override
public String execute() throws Exception {
String code = (String) session.get("authCode");
if (authCode != null & authCode.toUpperCase().equals(code)) {
return SUCCESS;
}
return INPUT;
}
}
index.jsp基本保持不变,略了。
struts.xml
<package name="default"namespace="/"extends="struts-default">
<action
name="authcode" class="action.AuthCodeAction">
<result
name="success" type="stream">
<param
name="contentType">image/jpeg</param>
</result>
</action>
<action
name="login" class="action.LoginAction">
<result>success.jsp</result>
<result
name="input">index.jsp</result>
</action>
</package>
测试:
验证码这块相对来说还算是比较简单。但是如果要做一些比较复杂的验证码的话还是要花不少心思的。这块本来跟Struts2没多大关系,不过既然刚好学到了,也就归如到Struts2中吧。
如有不妥之处,还请路过的老鸟们指正!小弟感激不尽。
分享到:
相关推荐
struts2学习笔记总结
struts2学习笔记,非本人所写,但有学习的价值,总结的很好,分享一个!
Struts2学习笔记,介绍了struts2的基础部分
这是学习struts2时记得重点笔记,包括了一些原理,ognl语句的编写,以及如何设置拦截器等等一些基本知识,起到复习和巩固的作用
struts2学习笔记3数据类型转换struts2学习笔记3数据类型转换struts2学习笔记3数据类型转换struts2学习笔记3数据类型转换struts2学习笔记3数据类型转换struts2学习笔记3数据类型转换struts2学习笔记3数据类型转换
struts2学习笔记struts2学习笔记struts2学习笔记
本人学习struts2的笔记,希望大家可以多多学习以后共同交流
1. struts2框架的引入 1)把struts2的相关jar包导入到项目中去 2)把struts2框架的配置文件struts.xml复制粘贴到项目中的src下面(同时也可以把log4j.properties放到src下) 在这里我们主要是要的这个struts.xml文件...
namespace :对应与项目名称后面的"/"(例如Struts2_0100_Introduction后面的"/") (http://localhost:8080/Struts2_0100_Introduction/) 四、 标签 是用来解决重名的问题,例如当系统的前台和后台都有一个action...
structs2很详细的学习笔记,structs2的建造,工作原理,例子,逐步讲解,纯文字的
struts2四天的学习笔记。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
Java相关课程系列笔记之十三Struts2学习笔记
个人收藏,纯属备份作用,做个记录,方便需要时候查看
Struts2 学习笔记.doc,Struts2 学习笔记.doc
struts2学习笔记三