JavaWeb-13:项目案例
用户注册和登录案例项目
一、用户注册和登录案例
1、技术架构:三层架构(表现层MVC:M:model V:View C:Controller)
2、要求:JSP中不能出现一行java脚本或java表达式。除了指令<%@%>,其余地方不能有<%%>
3、数据库:临时使用xml。解析使用Dom4j
4、必须知道要干什么?
5、开发步骤:
a、建立工程,搭建开发环境(拷贝jar包、建立配置文件)
dom4j.jar
jaxen.jar
commons-beanutils.jar
commons-logging.jar
standard.jar
jstl.jar
b、建立类所用的包
cn.itcast.domain:放JavaBean 弄出数据库
cn.itcast.dao:放Dao接口
cn.itcast.dao.impl:放Dao接口的实现
cn.itcast.servcie:业务接口
cn.itcast.service.impl:业务接口实现
cn.itcast.web.controller:Servlet控制器
//WEB-INF/pages:用户无法直接访问(只能靠转发)
工程架构:
知识点:用户输入数据的验证
1、客户端验证:使用js代码。减轻服务器的负担
2、服务器端验证:使用java代码。安全
3、实际开发中:1+2
二、用户的注册和登录案例攻略
1、搭建环境:
创建好需要用来读取和存储的xml数据库文件--->导入jar包(用来操作xml数据库文件的Dom4J包、封装数据用的BeanUtils包、使用XPath的jaxen-1.1-beta-6.jar包),创建User类(JavaBean),用于关联每一层的数据。
User.java
package com.heima.bean;
import java.io.Serializable;
import java.util.Date;
public class User implements Serializable{
private String username ;
private String password ;
private String email ;
private Date birthday ;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
}
2、设计业务逻辑层和数据访问层的接口
新建dao包:
接口类IUserDao:新建用于操作JavaBean数据的方法接口类IUserDao,里面提供添加用户并返回用户对象的方法addUser接口,提供根据用户的名字和密码查找用户,返回用户对象的方法findUserByUserNameAndPassword接口,提供根据用户的姓名查找用户的方法findUserByUserName接口。
IUserDao
package com.heima.dao;
import com.heima.bean.User;
public interface IUserDao {
/**
* 将用户添加到数据库
* @param user 要添加的用户
* @return 成功返回此用户,失败返回null
*/
public User addUser(User user) ;
/**
* 根据用户的名字和密码查找用户
* param username 用户名
* param password 用户的密码
* @return 找到用户返回此用户,否则返回null
*/
public User findUserByUserNameAndPassword(String username,String password ) ;
/**
* 根据用户的姓名查找用户
* @param username 用户的姓名
* @return 找到用户返回此用户,否则返回null
*/
public User findUserByUserName(String username) ;
}
新建Utils包:
DBUtils类:该工具类主要用于获取xml里的数据和把数据写回xml文件的方法:
getDocument()和write2xml()方法。该类里使用了dom4J包里的工具类,例如创建dom4j解析器和写回xml文件的方法。
DBUtils.java
package com.heima.utils;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
//工具类
public class DBUtils {
public static String path ;
static{
path = DBUtils.class.getClassLoader().getResource("users.xml").getPath() ;
}
//加载dom树
public static Document getDocument(){
try {
//创建dom4j解析器
SAXReader reader = new SAXReader() ;
//读取文档
Document document = reader.read(path) ;
return document ;
} catch (DocumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null ;
}
//将内存中的dom树写回到硬盘
public static void write2xml(Document document){
try {
XMLWriter writer = new XMLWriter(new FileOutputStream(path),OutputFormat.createPrettyPrint()) ;
writer.write(document) ;
writer.close() ;
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
新建dao.impl包:
该包用于存储继承接口类IUserDao的实现类UserDaoImpl.java。该类的作用:
addUser()主要是通过调用Utils包里提供的方法getDocument()和write2xml(),把从别处生成的User对象里的属性数据写到xml文件里并返回该User对象,findUserByUserNameAndPassword()主要是通过Utils包工具加载到xml里,然后通过dom4j里的方法使用XPath和相对应的属性定位到要找的user对象,并判断如果存在就封装成user对象并返回,无就返回null。
findUserByUserName()主要是通过传进来的属性并加载了xml文档后去使用XPath定位要找的user,如果不存在就返回一个新的User对象,若找到就返回null。
UserDaoImpl.java
package com.heima.dao.impl;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Node;
import com.heima.bean.User;
import com.heima.dao.IUserDao;
import com.heima.utils.DBUtils;
public class UserDaoImpl implements IUserDao {
public User addUser(User user) {
//获取dom树
Document document = DBUtils.getDocument() ;
//获得根节点
Element root = document.getRootElement() ;
//添加用户
root.addElement("user").addAttribute("username", user.getUsername())
.addAttribute("password", user.getPassword())
.addAttribute("email", user.getEmail())
.addAttribute("birthday", new SimpleDateFormat("yyyy-MM-dd").format(user.getBirthday())) ;
//写回硬盘
DBUtils.write2xml(document) ;
return user;
}
public User findUserByUserNameAndPassword(String username, String password) {
//加载dom树
Document document = DBUtils.getDocument() ;
//查找user节点
Node node = document.selectSingleNode("//user[@username='" + username + "' and @password='" + password+ "']") ;
if(node != null){
//封装数据
User user = new User() ;
Element el = (Element)node ;
user.setUsername(el.valueOf("@username")) ;
user.setPassword(el.valueOf("@password")) ;
user.setEmail(el.valueOf("@email")) ;
try {
user.setBirthday(new SimpleDateFormat("yyyy-MM-dd").parse(el.valueOf("@birthday"))) ;
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return user ;
}
return null;
}
public User findUserByUserName(String username) {
//加载dom树
Document document = DBUtils.getDocument() ;
//查找user节点
Node node = document.selectSingleNode("//user[@username='" + username + "']") ;
if(node != null){
return new User() ;
}
return null;
}
}
新建service包:
接口IUserService类:
该类的框架目的主要是让实现类去调用实现类UserDaoImpl.java里的功能。方法接口login():目的为了让实现类根据用户的姓名和密码完成用户的登录,方法接口:register()目的为了让实现类完成注册用户的功能。
IUserService.java
package com.heima.service;
import com.heima.bean.User;
import com.heima.exception.UserExistException;
public interface IUserService {
/**
* 根据用户的姓名和密码完成用户的登录
* @param username 用户的姓名
* @param password 用户的密码
* @return 登录成功返回此用户,失败返回null
*/
public User login(String username,String password ) ;
/**
* 完成注册用户的功能
* @param user 要注册的用户
* @return 成功返回TRUE,失败返回FALSE
* @throws UserExistException 如果用户存在抛出用户存在异常
*/
public boolean register(User user) throws UserExistException;
}
新建exception包:
UserExistException.java:
用于提供给UserServiceImpl类抛出异常。
UserExistException.java
package com.heima.exception;
public class UserExistException extends Exception {
}
新建service.impl包:
UserServiceImpl类:
在类里创建UserDaoImpl的对象,并使用UserDaoImpl对象里的方法去获得对应的User对象。
login():通过根据用户的姓名和密码来传入到UserDaoImpl对象的方法中去完成用户的登录,并返回User对象。
register():通过根据用户的姓名来传入到UserDaoImpl对象的方法中去完成用户的注册验证,并返回boolean值。还有判断如果用户存在抛出异常。
UserServiceImpl.java
package com.heima.service.impl;
import com.heima.bean.User;
import com.heima.dao.IUserDao;
import com.heima.dao.impl.UserDaoImpl;
import com.heima.exception.UserExistException;
import com.heima.service.IUserService;
public class UserServiceImpl implements IUserService {
IUserDao dao = new UserDaoImpl() ;
public User login(String username, String password) {
return dao.findUserByUserNameAndPassword(username, password);
}
public boolean register(User user) throws UserExistException {
//验证用户是否已经存在了
User u = dao.findUserByUserName(user.getUsername()) ;
if(u != null){
//说明此用户已经存在了,需要抛出异常
throw new UserExistException() ;
}
User u1 = dao.addUser(user) ;
return u1!=null ;
}
}
设计业务逻辑层和数据访问层的接口并实现步骤完成。
3、搭建表现层并连接各层间的关系
首先实现页面:新建index.jsp,写用于注册的register.jsp的超链和用于登录的login.jsp的超链
index.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="http://blog.163.com/faith_yee/blog/<%=basePath%>">
<title>My JSP 'index.jsp' starting page</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">
<!--
<link rel="stylesheet" type="text/css" href="http://blog.163.com/faith_yee/blog/styles.css">
-->
</head>
<body>
<a href="http://blog.163.com/faith_yee/blog/register.jsp">注册</a> <a href="http://blog.163.com/faith_yee/blog/login.jsp">登陆</a>
</body>
</html>
新建web.formbean包:
创建UserBean.java类:
该javabean类用于封装页面的数据和验证数据用的。类里除了各种属性和设置属性的方法之外,还有一个用来验证数据的方法validate(),就是从别处传来的各种属性都需要符合该方法里的规格,付过不符合,方法会为属性里的Map集合存储用于传达给页面的验证信息,并该方法最后返回boolean值反映存储的属性数据是否正确。
UserBean.java
package com.heima.web.formbean;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.beanutils.locale.converters.DateLocaleConverter;
//主要用来封装页面的数据,验证数据
public class UserBean {
private String username ;
private String password ;
private String repassword ;
private String email ;
private String birthday ;
private Map<String,String> errors = new HashMap<String,String>() ;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getRepassword() {
return repassword;
}
public void setRepassword(String repassword) {
this.repassword = repassword;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getBirthday() {
return birthday;
}
public void setBirthday(String birthday) {
this.birthday = birthday;
}
public Map<String, String> getErrors() {
return errors;
}
//检测数据
public boolean validate(){
//检测用户名
if(username == null || "".equals(username)){
errors.put("username", "用户名不能为空") ;
}else{
if(username.length() < 3 || username.length() > 8){
errors.put("username", "用户名长度不符合要求") ;
}
}
//检测密码
if(password == null || "".equals(password)){
errors.put("password", "密码不能为空") ;
}else{
if(password.length() < 3 || password.length() > 8){
errors.put("password", "密码长度不符合要求") ;
}
}
//检测重复密码
if(!repassword.equals(password)){
errors.put("password", "两次密码不一致") ;
}
//检测邮箱
String reg = "^([a-zA-Z0-9_\\-\\.]+)@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.)|(([a-zA-Z0-9\\-]+\\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\\]?)$" ;
if(email == null || "".equals(email)){
errors.put("email", "邮箱不能为空") ;
}else{
if(!email.matches(reg)){
errors.put("email", "邮箱格式不正确") ;
}
}
//检测出生日期
if(birthday == null || "".equals(birthday)){
errors.put("birthday", "出生日期不能为空") ;
}else{
// SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd") ;
// try {
// sdf.parse(birthday) ;
// } catch (ParseException e) {
// errors.put("birthday", "日期格式不正确") ;
// }
DateLocaleConverter dlc = new DateLocaleConverter() ;
try {
dlc.convert(birthday) ;
} catch (Exception e) {
errors.put("birthday", "日期格式不正确") ;
}
}
return errors.isEmpty() ;
}
}
在原来的Utils包:
新建WebUtils类:
该工具类用于给页面传进来的UserBean类中的属性数据还有页面发来的request请求进行使用反射技术生产对象:因为传来的类对象本来不存在,所以使用反射机制来处理生成对象,并使用BeanUtils包里的超级方法populate()把UserBean对象和页面中的属性和属性值包装起来,并返回。
WebUtils.java
package com.heima.utils;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.beanutils.BeanUtils;
//为页面服务的工具类
public class WebUtils {
public static <T> T fillFormBean(Class<T> clazz,HttpServletRequest reqeust){
T t = null ;
//创建对象
try {
t = clazz.newInstance() ;
BeanUtils.populate(t, reqeust.getParameterMap()) ;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return t ;
}
}
新建web.controls包:
新建RegisterServlet.java类:
用于处理页面register.jsp传进来的UserBean数据,并通过调用UserBean里的验证数据方法validate()验证页面传进来的数据是否有效,无效会跳转回register.jsp页面,然后BeanUtils.copyProperties()用于将数据从userBean中拷贝到user对象中,为存储到xml文档中的必然步骤。最后调用UserServiceImpl类中的register()方法注册用户,成功跳转到login.jsp页面,失败跳转回register.jsp页面。另外一种可能,如果register()抛出了异常,那么RegisterServlet.java就给异常处理。
RegisterServlet.java
package com.heima.web.controls;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.Date;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.locale.converters.DateLocaleConverter;
import com.heima.bean.User;
import com.heima.exception.UserExistException;
import com.heima.service.IUserService;
import com.heima.service.impl.UserServiceImpl;
import com.heima.utils.WebUtils;
import com.heima.web.formbean.UserBean;
//注册处理器
public class RegisterServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
// UserBean formuser = new UserBean() ;
// try {
// BeanUtils.populate(formuser, request.getParameterMap()) ;
// } catch (IllegalAccessException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// } catch (InvocationTargetException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
//接收页面数据,封装数据
UserBean userform = WebUtils.fillFormBean(UserBean.class, request) ;
//验证数据是否有效
if(!userform.validate()){
//没有通过
request.setAttribute("userform", userform) ;
request.getRequestDispatcher("/register.jsp").forward(request, response) ;
return ;
}
//验证通过
//将数据从userBean中拷贝到user对象中
User user = new User() ;
try {
//注册时间转换器
ConvertUtils.register(new DateLocaleConverter(), Date.class) ;
//拷贝数据
BeanUtils.copyProperties(user, userform) ;
} catch (Exception e) {
e.printStackTrace();
}
//注册用户
IUserService us = new UserServiceImpl() ;
try {
boolean flag = us.register(user) ;
if(flag){
//注册成功
response.getWriter().write("恭喜,注册成功,2秒后转向登陆页面") ;
response.setHeader("Refresh", "2;url=" + request.getContextPath() + "/login.jsp") ;
}else{
response.getWriter().write("注册失败,2秒后转注册页面") ;
response.setHeader("Refresh", "2;url=" + request.getContextPath() + "/register.jsp") ;
}
} catch (UserExistException e) {
//此用户已经注册过了
userform.getErrors().put("username", "此用户名已经注册了,请更换一个") ;
request.setAttribute("userform", userform) ;
request.getRequestDispatcher("/register.jsp").forward(request, response) ;
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
最后创建register.jsp:
利用el表达式把存到UserBean里的属性值通过submit按钮发送到指定的RegisterServlet里,register.jsp也会通过el表达式返回UserBean里Map类型的errors信息。
register.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="http://blog.163.com/faith_yee/blog/<%=basePath%>">
<title>My JSP 'register.jsp' starting page</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">
<!--
<link rel="stylesheet" type="text/css" href="http://blog.163.com/faith_yee/blog/styles.css">
-->
</head>
<script type="text/javascript">
function formsubmit(form){
var username = form.username.value ;
if(username == ""){
//alert("用户名不能为空") ;
//return false ;
}
return true ;
}
</script>
<body>
<form action = "${pageContext.request.contextPath }/servlet/RegisterServlet" method="post" onsubmit="return formsubmit(this)">
<table border =1 >
<tr>
<td>${"用户名" }</td>
<td><input type="text" name = "username" value = "${userform.username}">${userform.errors.username }</td>
</tr>
<tr>
<td>${"密码" }</td>
<td><input type="password" name = "password" value = "${userform.password }">${userform.errors.password }</td>
</tr>
<tr>
<td>${"确认密码" }</td>
<td><input type="password" name = "repassword" ></td>
</tr>
<tr>
<td>${"邮箱" }</td>
<td><input type="text" name = "email" value = "${userform.email }">${userform.errors.email }</td>
</tr>
<tr>
<td>${"出生日期" }</td>
<td><input type="text" name = "birthday" value = "${userform.birthday }">${userform.errors.birthday }</td>
</tr>
<tr>
<td colspan = "2"><input type="submit" value = "注册"></td>
</tr>
</table>
</form>
</body>
</html>
还有其他类和jsp文件:如下:
login.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="http://blog.163.com/faith_yee/blog/<%=basePath%>">
<title>My JSP 'register.jsp' starting page</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">
<!--
<link rel="stylesheet" type="text/css" href="http://blog.163.com/faith_yee/blog/styles.css">
-->
</head>
<script type="text/javascript">
function formsubmit(form){
var username = form.username.value ;
if(username == ""){
//alert("用户名不能为空") ;
//return false ;
}
return true ;
}
</script>
<body>
${errorinfo }
<form action = "${pageContext.request.contextPath }/servlet/LoginServlet" method="post" onsubmit="return formsubmit(this)">
<table border =1 >
<tr>
<td>${"用户名" }</td>
<td><input type="text" name = "username" value = "${userform.username}">${userform.errors.username }</td>
</tr>
<tr>
<td>${"密码" }</td>
<td><input type="password" name = "password" value = "${userform.password }">${userform.errors.password }</td>
</tr>
<tr>
<td colspan = "2"><input type="submit" value = "登陆"></td>
</tr>
</table>
</form>
</body>
</html>
LoginServlet.java
package com.heima.web.controls;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.heima.bean.User;
import com.heima.service.IUserService;
import com.heima.service.impl.UserServiceImpl;
//实现登陆功能
public class LoginServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
//拿取页面传递的数据
String username = request.getParameter("username") ;
String password = request.getParameter("password") ;
//查找用户
//创建service层对象
IUserService us = new UserServiceImpl() ;
User user = us.login(username, password) ;
if(user != null){
//登陆成功
request.getSession().setAttribute("loginuser", user) ;
response.getWriter().write("登陆成功,2秒后转向主页") ;
response.setHeader("Refresh", "2;url="+ request.getContextPath() + "/main.jsp") ;
}else{
//登陆失败
request.setAttribute("errorinfo", "用户名或者密码错误") ;
request.getRequestDispatcher("/login.jsp").forward(request, response) ;
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
main.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="http://blog.163.com/faith_yee/blog/<%=basePath%>">
<title>My JSP 'register.jsp' starting page</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">
<!--
<link rel="stylesheet" type="text/css" href="http://blog.163.com/faith_yee/blog/styles.css">
-->
</head>
<body>
${loginuser.username },欢迎你
</body>
</html>
表现层完成。
三、总结
用户--->register.jsp--->输入信息------>提交到RegisterServlet里,RegisterServlet结合UserBean类、UserServiceImpl类给用户注册----->注册成功后:数据会转到User类里,并通过业务逻辑层和数据访问层的类和方法把User类里的数据存储到xml数据库中。可以发现,我们搭建这个项目是和用户的思维完全相反的。