简单的登陆注册的实现+验证码和表单验证

时间:2022-05-06 19:32:42

昨天做了一个登录注册的小实例今天回忆一遍并且通过博客的方式写下来


首先先po上JavaWeb 的经典三层框架
简单的登陆注册的实现+验证码和表单验证

注意 的是每个层都应该完成自己应该完成的不能完成别的层该做的事,不然耦合性太高

首先先确定登陆和注册需要两个页面 login.jsp regist.jsp 再加上一个欢迎首页 welcome.jsp 登陆成功才能看见 (session 实现)

需要LoginServlet.java 和 RegistServlet.java
因为有验证码 所以再创建一个 VerifyCodeServlet.java
servlet 用来处理页面请求 然后调用方法 把结果响应到页面
然后创建一个实体类 与登陆注册先关的实体类 User.java

业务逻辑层实现与登陆注册相关业务

数据层 以xml 代替

<?xml version="1.0" encoding="UTF-8"?>

<users>
<user username="Ashin" password="whoami"/>
<user username="Ben" password="whoami"/>

</users>

分析完了然后导入项目所需要的包
commons-logging-1.1.3 jar //用来把map中的数据封装到bean对象中
commons-beanutils-1.9.2.jar
dom4j-1.6.1.jar //以下两个是解析xml用
jaxen-1.1.6.jar
ashin-utils.jar // 自己生成的工具包 里面有生成验证码的代码

自定义jar包中的代码:

public static <T>T toBean(Map map,Class<T> clazz){

try {
/*
* 创建指定类型的javabean对象
*/


//System.out.println(map); //有值{Ben=whoami2, Ashin=whoami, Aken=whoami3}

T bean = clazz.newInstance();

/*
* 把数据封装到bean中
*/

BeanUtils.populate(bean,map);
//System.out.println(bean); //无值
/*
* 返回javabean对象
*/

return bean;
} catch (Exception e) {
throw new RuntimeException(e);
}

}
public class VerifyCode {
private int w =70;
private int h =35;

private Random r = new Random();
//定义有那些字体
private String[] fontNames ={"宋体","华文楷体","黑体","微软雅黑","楷体_GB2312"};
//定义有那些验证码的随机字符
private String codes = "23456789abcdefghjkmnopqrstuvwxyzABCDEFGHJKLMNOPQRSTUVWXYZ";
//生成背景色
private Color bgColor = new Color(250,250,250);
//用于gettext 方法 获得生成的验证码文本
private String text;

//生成随机颜色
private Color randomColor(){
int red =r.nextInt(150);
int green =r.nextInt(150);
int blue =r.nextInt(150);
return new Color(red,green,blue);
}

//生成随机字体
private Font randomFont(){
int index = r.nextInt(fontNames.length);
String fontName = fontNames[index];
int style =r.nextInt(4);
int size =r.nextInt(5)+24;

return new Font(fontName, style, size);
}

//画干扰线
private void drawLine(BufferedImage image){
int num = 3;
Graphics2D g2 = (Graphics2D) image.getGraphics();
for(int i =0;i <num;i++){
int x1 = r.nextInt(w);
int y1 = r.nextInt(h);
int x2 = r.nextInt(w);
int y2 = r.nextInt(h);
g2.setStroke(new BasicStroke(1.5F));//不知道
g2.setColor(Color.blue);
g2.drawLine(x1, y1, x2, y2);
}
}

//得到codes的长度内的随机数 并使用charAt 取得随机数位置上的codes中的字符
private char randomChar(){
int index =r.nextInt(codes.length());
return codes.charAt(index);
}

//创建一张验证码的图片
public BufferedImage createImage(){
BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
Graphics2D g2 =(Graphics2D) image.getGraphics();
StringBuilder sb = new StringBuilder();
//向图中画四个字符
for(int i= 0 ;i < 4; i++) {
String s = randomChar()+"";
sb.append(s);
float x = i * 1.0F * w / 4;
g2.setFont(randomFont());
g2.setColor(randomColor());
g2.drawString(s,x,h-5);

}
this.text = sb.toString();
drawLine(image);

//返回图片
return image;

}

//得到验证码的文本 后面是用来和用户输入的验证码 检测用
public String getText(){
return text;
}

//定义输出的对象和输出的方向
public static void output(BufferedImage bi,OutputStream fos) throws FileNotFoundException, IOException{
ImageIO.write(bi, "JPEG", fos);
}

创建包

com.ashin.beans     对应实体类
> User.java
>username
>password
>verifycode
com.ashin.service 业务层
>UserService.java
>UserException.java //自定义异常类
com.ashin.web.servlet web层
>LoginServlet.java
>RegistServlet.java
>VerifyCodeServlet
com.ashin.dao 数据层
>UserDao.java

下面贴部分代码 并会注释

实体类就不多写了 只是多了个VerifyCode 属性用于验证码

先把userservice 贴出来以便后面的理解


/**
* 第一个方法通过名字查询用户返回一个用户对象 调用的是userdao的findUserByName()方法
* 并通过adduser()在数据库中添加一个用户
* 第二个方法 登陆时调用 来判断用户名是否存在 密码是否正确
* 返回的userexception都来自自定义异常类
* @param user
* @throws UserException
*/

private UserDao userdao = new UserDao();

public void regist(User user) throws UserException{
User _user = userdao.findUserByName(user.getUsername());
if(_user != null ) throw new UserException("用户名"+user.getUsername()+",已经被注册了");

userdao.addUser(user);
}

public User login(User form) throws UserException {
User _user = userdao.findUserByName(form.getUsername());
if(_user == null) throw new UserException("用户名"+form.getUsername()+",不存在请去注册");

if( ! _user.getPassword().equals(form.getPassword())) throw new UserException("密码不正确");
return _user;

}

自定义异常类

创建UserException 类 只需继承Exception 类并实现父类构造方法即可

regist.jsp

<script type="text/javascript">
function _change(){

var ele = document.getElementById("verifyCode");
ele.src="<c:url value='/VerifyCodeServlet'/>?xxx="+new Date().getTime();
//只要后面的参数变化的话就会再次发送请求实现验证码换一张的效果
}
</script>
</head>
/**
注释块:
这边的${msg } 用于显示错误信息 如果RegistServlet 捕获到错误会把异常加入request 域 并转发到 次页面进行输出
<c:url value="/LoginServlet"/>
需要引入标签库 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
input标签中的el表达式用于回显 用户名及密码 后面的${errors.password }是显示表单验证时返回的错误信息
*/
<body>
<h1>注册</h1>
<p style="color: red ; font-weight: 900">${msg }</p>
<form action='<c:url value="/RegistServlet"/>' method="post">
用户名:<input type="text" name="username" value="${user.username }"/><font color="red" >${errors.username }</font>
<br><br>
密 码:<input type="password" name ="password" value="${user.password }"/><font color="red" >${errors.password }</font><br>
//验证码 src="<c:url value='/VerifyCodeServlet'/>" 指向/项目名/VerifyCodeServlet
验证码:<input type="text" name="verifyCode" value="${user.verifyCode }" size="3"/>
<img id ="verifyCode" src="<c:url value='/VerifyCodeServlet'/>"/><font color="red" >${errors.verifycode }</font><br>
<a href="Javascript:_change()">换一张</a>
<input type="submit" value="提交">
</form>
</body>

Regist.servlet

public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");


UserService userService = new UserService();
//将表单信息封装到user对象中
User form = CommonUtils.toBean(request.getParameterMap(), User.class);
//创建一个map对象用来存储错误信息
Map<String, String> errors = new HashMap<String, String>();
/**
* 下面三段都是表单验证
*/

String username = form.getUsername();
if(username == null || username.trim().isEmpty()){
errors.put("username", "用户名不能为空");
}else if(username.length()<3 || username.length()>13){
errors.put("username", "用户名长度应该为3~13位");
}

String password = form.getPassword();
if(password == null || password.trim().isEmpty()){
errors.put("password", "密码不能为空");
}else if(password.length()<3 || password.length()>13){
errors.put("password", "密码长度应该为3~13位");
}

String createverify = (String) request.getSession().getAttribute("session_vf");
String verifycode = form.getVerifyCode();
if(verifycode == null || verifycode.trim().isEmpty()){
errors.put("verifycode", "验证码不能为空");
}else if(verifycode.length() != 4){
errors.put("verifycode", "验证码必须是四位");
}else if(!verifycode.equalsIgnoreCase(createverify)){
errors.put("verifycode", "验证码错误");
}

/*
* 查看是否有异常 没就执行下面的代码 有就保存异常 保存用于回显的信息 并转发到注册页面 注意return
*/

if(errors != null && errors.size() > 0){
request.setAttribute("errors", errors);
request.setAttribute("user", form);
request.getRequestDispatcher("/user/Regist.jsp").forward(request, response);
return; //不往下执行
}


//上面没有异常 就执行 调用 业务逻辑类的 regist方法 如果没有异常就显示成功 有异常就保存异常信息 转发到注册页面 同上
try {
userService.regist(form);

response.getWriter().print("注册成功<br>");
response.getWriter().print("<a href='"+request.getContextPath()+"/user/Login.jsp"+"'>点击这里去登录</a>");
} catch (UserException e) {
request.setAttribute("msg", e.getMessage());
request.setAttribute("user", form);
request.getRequestDispatcher("/user/Regist.jsp").forward(request, response);

}

}

login.jsp


<body>
<h1>登陆</h1>
<p style="color: red ; font-weight: 900">${msg }</p>
<form action='<c:url value="/LoginServlet"/>' method="post">
用户名:<input type="text" name="username" value="${user.username }"/>
<br><br>
密 码:<input type="password" name ="password" value="${user.password }"/>
<br>
<input type="submit" value="登陆">
</form>
</body>

LoginServlet
//与注册的servlet 类似 调用的业务逻辑方法不同

public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset:utf-8");

UserService userService = new UserService();

User form = CommonUtils.toBean(request.getParameterMap(), User.class);


try {
User user = userService.login(form);
request.getSession().setAttribute("sessionUser", user);
response.sendRedirect(request.getContextPath()+"/user/Welcome.jsp");
} catch (UserException e) {
request.setAttribute("msg", e.getMessage());
request.setAttribute("user", form);
request.getRequestDispatcher("/user/Login.jsp").forward(request, response);
}
}
//get方法 因为前面刷新验证码 采用的是 +参数的形式 
//所用的类在开头自定义jar包中的代码
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
/**
* 创建验证码类
*/

VerifyCode vf = new VerifyCode();
/**
* 生成验证码图片
*/

BufferedImage image = vf.createImage();
/**
* 把验证码文本存在session中
*/

request.getSession().setAttribute("session_vf", vf.getText());
/**
* 把图片相应给客户端
*/

VerifyCode.output(image, response.getOutputStream());



}

最后贴上userdao 的代码

package com.ashin.dao;


import java.io.FileOutputStream;
import java.io.OutputStreamWriter;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;

import com.ashin.domain.User;

/**
* 数据类
* @author Ashin
*
*/

public class UserDao {

String path ="F://users.xml";

public User findUserByName(String username){

/**
* 获得一个解析器
*/

SAXReader reader = new SAXReader();
/**
* 通过路径取得一个文档对象
*/

try {
Document doc = reader.read(path);
/**
* 通过xpah查询得到element
*/

Element element = (Element) doc.selectSingleNode("//user[@username='"+username+"']");
/**
* 查看是否存在 返回null则不存在 直接返回
*/

if(element == null) return null;
/**
* 把element数据封装到user对象中
*/

User u = new User();

String attrUsername = element.attributeValue("username");
String attrPassword = element.attributeValue("password");

u.setUsername(attrUsername);
u.setPassword(attrPassword);
return u;
} catch (Exception e) {
throw new RuntimeException(e);
}


}

public boolean addUser(User user ){

SAXReader reader = new SAXReader();
try {
Document document = reader.read(path);
/**
* 获得文档的根节点
*/

Element root = document.getRootElement();
/**
* 在根节点下添加子元素
*/

Element userEle = root.addElement("user");

/**
* 设置元素的属性
*/

userEle.addAttribute("username", user.getUsername());
userEle.addAttribute("password", user.getPassword());

/**
* 保存文档
*/

OutputFormat outputFormat = new OutputFormat("\t",true); //缩进使用\t 换行选择true
outputFormat.setTrimText(true);//是否删除原有的换行和缩进

try {
XMLWriter writer = new XMLWriter(new OutputStreamWriter(new FileOutputStream(path), "utf-8"),outputFormat);
writer.write(document);
writer.close();
} catch (Exception e) {
throw new RuntimeException(e);
}


} catch (DocumentException e) {
throw new RuntimeException(e);
}

return false;

}
}

此次实例让我很直观的理解到了三层框架的好处,每个层都各司其职 完成对应的功能