【电信计费系统项目实战】基础篇---登录界面实现
验证码,登录界面,struts,map2016-08-10
这一篇很简单,登录界面无非就是账号密码框再加上一个验证码,这里我们主要了解生成验证码及验证的基本原理,可以参考我之前写的一篇ajax注册页面异步验证。
登录界面代码:
<%@page pageEncoding="utf-8"%>
<%@taglib uri="/struts-tags" prefix="s" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>达内-NetCTOSS</title>
<link type="text/css" rel="stylesheet" media="all" href="../styles/global.css" />
<link type="text/css" rel="stylesheet" media="all" href="../styles/global_color.css" />
<script type="text/javascript" language="javascript">
function change(imgObj) {
imgObj.src = "createImage?date=" + new Date().getTime();
}
</script>
</head>
<body class="index">
<div class="login_box">
<!-- method="post",保证中文不乱码 -->
<form action="login" method="post">
<table>
<tr>
<td class="login_info">账号:</td>
<td colspan="2"><input name="adminCode" type="text" value="<s:property value="adminCode"/>" class="width150" /></td>
<td class="login_error_info"><span class="required">30长度的字母、数字和下划线</span></td>
</tr>
<tr>
<td class="login_info">密码:</td>
<td colspan="2"><input name="password" type="password" value="<s:property value="password"/>" class="width150" /></td>
<td><span class="required">30长度的字母、数字和下划线</span></td>
</tr>
<tr>
<td class="login_info">验证码:</td>
<td class="width70"><input name="imageCode" type="text" class="width70" /></td>
<td><img src="createImage" onclick="change(this)" alt="验证码" title="点击更换" /></td>
<td><span class="required"></span></td>
</tr>
<tr>
<td></td>
<td class="login_button" colspan="2">
<a href="javascript:document.forms[0].submit();"><img src="../images/login_btn.png" /></a>
</td>
<td><span class="required"><s:property value="errorMsg"/></span></td>
</tr>
</table>
</form>
</div>
</body>
</html>
代码解释:验证码图片那一行代码,通过点击超链接的方式来变换验证码图片上的验证码:
function change(imgObj) {
imgObj.src = "createImage?date=" + new Date().getTime();
}
显而易见,通过createImage请求来变换验证码的,接着看到底是哪个Action用来生成验证码的:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN"
"http://struts.apache.org/dtds/struts-2.1.7.dtd">
<struts>
<!-- 登录模块 -->
<package name="login" namespace="/login"
extends="struts-default">
<!--
跳转到登录页面Action。
该Action不需要写业务代码,class可以省略。
当class省略时,Struts2会自动调用ActionSupport,
这个类中有默认的方法execute,返回success。
-->
<action name="toLogin">
<result name="success">
/WEB-INF/main/login.jsp
</result>
</action>
<!-- 登录校验Action -->
<action name="login"
class="com.tarena.action.login.LoginAction">
<!-- 成功时跳转到首页 -->
<result name="success">
/WEB-INF/main/index.jsp
</result>
<!-- 失败时回登录页 -->
<result name="fail">
/WEB-INF/main/login.jsp
</result>
</action>
<!-- 生成验证码Action -->
<action name="createImage"
class="com.tarena.action.login.CreateImageAction">
<result name="success" type="stream">
<!--
param是参数注入的标签,用于
给组件的属性注入值。这里相当于是给
stream类型的Result的inputName属性
设置值,其值为"imageStream".
-->
<param name="inputName">
imageStream
</param>
</result>
</action>
</package>
</struts>
以上是登录模块的struts配置,我们可以看到CreateImageAction类生成验证码操作
<!-- 生成验证码Action -->
<action name="createImage"
class="com.tarena.action.login.CreateImageAction">
<result name="success" type="stream">
<!--
param是参数注入的标签,用于
给组件的属性注入值。这里相当于是给
stream类型的Result的inputName属性
设置值,其值为"imageStream".
-->
<param name="inputName">
imageStream
</param>
</result>
</action>
CreateImageAction完成的功能步骤:
package com.tarena.action.login;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import com.tarena.action.BaseAction;
import com.tarena.util.ImageUtil;
public class CreateImageAction extends BaseAction {
//output
private InputStream imageStream;
public String execute() {
//1.生成图片
Map<String, BufferedImage> map =
ImageUtil.createImage();
//2.读取出验证码,并放入session
String code = map.keySet().iterator().next();
session.put("imageCode", code);
//3.读取出图片
BufferedImage image = map.get(code);
try {
imageStream = ImageUtil.getInputStream(image);
} catch (IOException e) {
e.printStackTrace();
return "error";
}
return "success";
}
public InputStream getImageStream() {
return imageStream;
}
public void setImageStream(InputStream imageStream) {
this.imageStream = imageStream;
}
}
(map的keySet()方法返回值是Map中key值的集合)
由此可以看出,具体完成验证码的是工具类ImageUtil,只不过CreateImageAction 调用它而已,它完成的功能有:
1. 用于生成一个图片,图片中带有验证码。
2. Map的key存的是验证码字符串
3. Map的value存的是图片
此处灵活的运用map这个容器,非常方便的存取值。ImageUtil里都是java里最基本的知识,没什么好讲的,看看就行了:
package com.tarena.util;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGImageEncoder;
public final class ImageUtil {
private static final char[] chars = { '0', '1', '2', '3', '4', '5', '6',
'7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I' };
private static final int SIZE = 4;
private static final int LINES = 5;
private static final int WIDTH = 80;
private static final int HEIGHT = 35;
private static final int FONT_SIZE = 30;
/**
* 用于生成一个图片,图片中带有验证码。
* Map的key存的是验证码字符串,
* Map的value存的是图片。
*/
public static Map<String, BufferedImage> createImage() {
StringBuffer sb = new StringBuffer();
BufferedImage image = new BufferedImage(WIDTH, HEIGHT,
BufferedImage.TYPE_INT_RGB);
Graphics graphic = image.getGraphics();
graphic.setColor(Color.LIGHT_GRAY);
graphic.fillRect(0, 0, WIDTH, HEIGHT);
Random ran = new Random();
// 画随机字符
for (int i = 1; i <= SIZE; i++) {
int r = ran.nextInt(chars.length);
graphic.setColor(getRandomColor());
graphic.setFont(new Font(null, Font.BOLD + Font.ITALIC, FONT_SIZE));
graphic.drawString(chars[r] + "", (i - 1) * WIDTH / SIZE,
2*HEIGHT/3);
sb.append(chars[r]);// 将字符保存,存入Session
}
// 画干扰线
for (int i = 1; i <= LINES; i++) {
graphic.setColor(getRandomColor());
graphic.drawLine(ran.nextInt(WIDTH), ran.nextInt(HEIGHT),
ran.nextInt(WIDTH), ran.nextInt(HEIGHT));
}
Map<String, BufferedImage> map = new HashMap<String, BufferedImage>();
map.put(sb.toString(), image);
return map;
}
public static Color getRandomColor() {
Random ran = new Random();
Color color = new Color(ran.nextInt(256), ran.nextInt(256),
ran.nextInt(256));
return color;
}
public static InputStream getInputStream(BufferedImage image)
throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(bos);
encoder.encode(image);
byte[] imageBts = bos.toByteArray();
InputStream in = new ByteArrayInputStream(imageBts);
return in;
}
}
然后就是登陆验证了,也没什么好讲的(代码如下),其他内容后面的章节会讲到。
package com.tarena.action.login;
import org.omg.CORBA.Request;
import com.tarena.action.BaseAction;
import com.tarena.dao.DAOException;
import com.tarena.dao.DAOFactory;
import com.tarena.dao.login.ILoginDAO;
import com.tarena.entity.Admin;
public class LoginAction extends BaseAction {
//input
private String adminCode;
private String password;
private String imageCode;
//output
private String errorMsg;
public String execute() {
//1.校验验证码
String code = (String) session.get("imageCode");
if(code == null ||
!code.equalsIgnoreCase(imageCode)) {
//验证码不匹配,校验失败
errorMsg = "验证码错误!";
return "fail";
}
//2.根据账号查询管理员
ILoginDAO dao = DAOFactory.getLoginDAO();
Admin admin = null;
try {
admin = dao.findByCode(adminCode);
} catch (DAOException e) {
e.printStackTrace();
errorMsg = "查询管理员报错,请联系系统管理员!";
return "fail"; //异常,返回登录页面
}
if(admin == null) {
//3.如果没有对应的管理员,登录失败,账号不存在
errorMsg = "账号不存在!";
return "fail"; //验证失败,回登录页面
} else {
//4.如果有对应管理员,看密码,若密码错误,登录失败
String pwd = admin.getPassword();
if(!pwd.equals(password)) {
errorMsg = "密码错误!";
return "fail"; //验证失败,回登录页面
}
}
//4.登录成功后,将登录信息记录到Session
session.put("admin", adminCode);
return "success"; //成功,去index.jsp
}
public String getAdminCode() {
return adminCode;
}
public void setAdminCode(String adminCode) {
this.adminCode = adminCode;
}
public String getErrorMsg() {
return errorMsg;
}
public void setErrorMsg(String errorMsg) {
this.errorMsg = errorMsg;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getImageCode() {
return imageCode;
}
public void setImageCode(String imageCode) {
this.imageCode = imageCode;
}
}
很多网站都会加个验证码,比如百度贴吧啥的,验证码一般是防止有人利用机器人自动批量注册、对特定的注册用户用特定程序暴力破解方式进行不断的登陆、灌水。因为验证码是一个混合了数字或符号的图片,人眼看起来都费劲,机器识别起来就更困难。map可以很好的存取字符串和图片来实现验证码,所以这里主要了解map的用法就行啦!