实验八 MVC模式
一、实验要求
用MVC模式来改写用户登录验证程序。
二、实验步骤/代码
2.1项目结构
如果需要功能更完善、代码更简洁以及bug更少的代码,可以到我的小程序 航筱北同学上搜索查看。里面还有很多实战项目和面试话术。
2.2实现代码
实现了model模型层、view视图层、controller控制层的分离。
具体代码见文档尾,可点击链接跳转查看
三、实验结果
运行截图:
- 启动server,进入localhost:8080
- 根据提示,点击跳转到登录页面进行登录
- 输入信息登录,发现用户名不存在,原因是数据库没有这个用户名。再点击去注册,跳转至注册页面。
- 首先,密码长度有限制,登录名也有限制,如果存在用户名,会提示用户名已存在。
- 满足要求填写注册表单后,提示注册成功,而且此时,刷新数据库,会看到刚刚注册的信息。
- 根据提示我们来到登录页登录,并试着故意输错密码,显示密码错误。
- 输入正确密码后,成功进入。获取数据库信息并显示到页面。
附:
实现代码:
package domain;
public class User {
private String username;
private String password;
private String verifyCode;
private int age;
private String gender;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String getVerifyCode() {
return verifyCode;
}
public void setVerifyCode(String verifyCode) {
this.verifyCode = verifyCode;
}
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;
}
@Override
public String toString() {
return super.toString();
}
} |
package dao;
import domain.User;
public interface UserDao {
public User findByUsername(String username);
public void add(User user);
} |
package util;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
public class JdbcUtils {
private static Properties properties = null;
static {
//家在配置文件到properties文件中
try {
InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("dbconfig.properties");
properties = new Properties();
properties.load(in);
} catch (Exception e) {
throw new RuntimeException(e);
}
try {
Class.forName(properties.getProperty("driverClassName"));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(properties.getProperty("url"),
properties.getProperty("username"),
properties.getProperty("password"));
}
} |
package dao;
import domain.User;
import util.JdbcUtils;
import java.sql.*;
public class JdbcUserDaoImpl implements UserDao {
@Override
public User findByUsername(String username) {
Connection connection = null;
PreparedStatement ps = null;
ResultSet rs = null;
User user = new User();
try {
connection = JdbcUtils.getConnection();
String sql = "SELECT * FROM t_user WHERE username=?";
ps = connection.prepareStatement(sql);
ps.setString(1, username);
rs = ps.executeQuery();
if (rs == null) return null;
if (rs.next()) {
user.setUsername(rs.getString("username"));
user.setPassword(rs.getString("password"));
user.setAge(rs.getInt("age"));
user.setGender(rs.getString("gender"));
return user;
} else {
return null;
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
try {
if (rs != null) rs.close();
if (ps != null) ps.close();
if (connection != null) connection.close();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
@Override
public void add(User user) {
Connection connection = null;
PreparedStatement ps = null;
try {
connection = JdbcUtils.getConnection();
String sql = "insert into t_user values(?,?,?,?)";
ps = connection.prepareStatement(sql);
ps.setString(1, user.getUsername());
ps.setString(2, user.getPassword());
ps.setInt(3, user.getAge());
ps.setString(4, user.getGender());
ps.executeUpdate();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
try {
if (ps != null) ps.close();
if (connection != null) connection.close();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
} |
package dao;
import java.io.InputStream;
import java.util.Properties;
public class DaoFactory {
private static Properties properties = null;
static {
//加在配置文件到properties文件中
try {
InputStream in = DaoFactory.class.getClassLoader().getResourceAsStream("dao.properties");
properties = new Properties();
properties.load(in);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
//返回一个具体实现UserDao的实现类
public static UserDao getUserDao() {
/*
*给出一个配置文件,文件中给出UserDao接口的实现类名称
* 我们这个方法,获取实现类的类名,通过反射创建对象
*/
try {
Class clazz = Class.forName(properties.getProperty("dao.UserDao"));
return (UserDao) clazz.newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
} |
UserException.java
package service;
public class UserException extends Exception {
public UserException(String message) {
super(message);
}
} |
UserService.java
package service;
import dao.DaoFactory;
import dao.UserDao;
import domain.User;
public class UserService {
UserDao userDao = DaoFactory.getUserDao();
public void regist(User user) throws UserException {
User _user = userDao.findByUsername(user.getUsername());
if (_user != null) {
throw new UserException("用户名" + user.getUsername() + "已被注册");
}
userDao.add(user);
}
public User login(User user) throws UserException {
User _user = userDao.findByUsername(user.getUsername());
if (_user == null) {
throw new UserException("用户名不存在");
}
if (!user.getPassword().equals(_user.getPassword())) {
throw new UserException("密码错误");
}
return _user;
}
} |
CommonUtils.java
package util;
import org.apache.commons.beanutils.BeanUtils;
import java.util.Map;
import java.util.UUID;
public class CommonUtils {
public static <T> T toBean(Map map, Class<T> clazz) {
try {
T bean = clazz.newInstance();
BeanUtils.populate(bean, map);
return bean;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
} |
LoginServlet.java
package servlet;
import domain.User;
import service.UserException;
import service.UserService;
import util.CommonUtils;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class LoginServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
/*
* 获取表单数据,将用户名在数据库中查找
* 1.成功,比较得到的密码和表单中的密码是否一样,一样则成功,不一样则返回密码错误信息
* 2.失败,返回用户名错误的信息
*/
User form=CommonUtils.toBean(request.getParameterMap(),User.class);
UserService userService=new UserService();
try {
User user= userService.login(form);
request.getSession().setAttribute("sessionUser",user);
response.sendRedirect(request.getContextPath()+"/user/welcome.jsp");
} catch (UserException e) {
request.setAttribute("user",form);
request.setAttribute("msg",e.getMessage());
request.getRequestDispatcher("/user/login.jsp").forward(request,response);
}
}
} |
RegistServlet.java
package servlet;
import domain.User;
import service.UserException;
import service.UserService;
import util.CommonUtils;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class RegistServlet extends HttpServlet {
protected 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);
//校验用户名、密码是否为空,长度要在3-15之间,验证码要正确
Map<String,String> errors=new HashMap<>();
String username=form.getUsername();
if (username==null||username.trim().isEmpty())
{
errors.put("username","用户名不能为空");
}else if (username.length()<2||username.length()>15)
{
errors.put("username","用户名长度应该在2~15之间");
}
String password=form.getPassword();
if (password==null||password.trim().isEmpty())
{
errors.put("verifyCode","密码不能为空");
}else if (password.length()<3||password.length()>15)
{
errors.put("password","密码长度应该在3~15之间");
}
String verifyCode=form.getVerifyCode();
String sessionVerifyCode=(String) request.getSession().getAttribute("session_vcode");
if (verifyCode==null||verifyCode.trim().isEmpty())
{
errors.put("verifyCode","验证码不能为空");
}else if (verifyCode.length()!=4)
{
errors.put("verifyCode","验证码长度应该在为4位");
}else if (!sessionVerifyCode.equalsIgnoreCase(verifyCode))
{
errors.put("verifyCode","验证码错误");
}
if (errors!=null&&errors.size()>0)
{
request.setAttribute("errors",errors);
request.setAttribute("user",form);
request.getRequestDispatcher("/user/regist.jsp").forward(request,response);
return;
}
try {
userService.regist(form);
response.getWriter().println("<h1>注册成功</h1><a href='"+request.getContextPath()+"/user/login.jsp"+"'>点击这里去登录</a>");
} catch (UserException e) {
request.setAttribute("user",form);
request.setAttribute("msg",e.getMessage());
request.getRequestDispatcher("/user/regist.jsp").forward(request,response);
}
}
} |
VerifyCode.java
package util;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Random;
public class VerifyCode {
private int width = 70;//设置图片缓冲区的宽
private int height = 35;//设置图片缓冲区的高
private Random r = new Random();//生成随机数字
private String[] fontNames = {"宋体", "华文楷体", "黑体", "微软雅黑"};//创建一个字体数组
private Color bgColor = new Color(255, 255, 255);//创建图片的白色背景
private String codes = "23456789zxcvbnmasdfgjkqwertyui";//可供选择的随机文字
private String text;//在图片上生成的文本
/*
* 创建图片缓冲区
*/
public BufferedImage getImage() {
BufferedImage image = createImage();//1.调用创建图片缓冲区方法
Graphics g = image.getGraphics();//3.得到绘制环境
StringBuilder sb = new StringBuilder();//用来装载生成的验证码文本
/*
循环四次
每次生成一个字符
*/
for (int i = 0; i < 4; i++) {
String str = randomChar() + "";//调用产生随机字符方法,随机生成一个字符
sb.append(str);//将生成的随机字符加到sb后面
g.setFont(randomFont());//调用产生随机字体方法
g.setColor(randomColor());//调用产生随机颜色方法
g.drawString(str, i * width / 5, height - 5);//在图片中绘制文本
}
this.text = sb.toString();//把生成字符串赋给文本
drawLine(image);//调用添加干扰线方法对图片中的文本进行干扰
return image;
}
/*
* 返回验证码图片上的文本,
* 此方法必须在getImage()方法调用之后
*
*/
public String getText() {
return this.text;
}
/*
*保存图片到指定的输出流
*/
public static void output(BufferedImage image, OutputStream out) throws IOException {
ImageIO.write(image, "JPEG", out);
}
/*
*创建图片缓冲区方法
*/
private BufferedImage createImage() {
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics g = image.getGraphics();
g.setColor(bgColor);
g.fillRect(0, 0, width, height);
return image;
}
/*
* 生成随机字符方法
*/
public char randomChar() {
int index = r.nextInt(codes.length());
char y = codes.charAt(index);
return y;
}
/*
*生成随机字体
*/
public Font randomFont() {
int index = r.nextInt(fontNames.length);
int style = r.nextInt(4);//设置字体样式,0表示无样式,1表示粗体,2表示斜体,3表示粗体加斜体
int size = r.nextInt(4) + 19;//设置字体大小,范围在24-27之间
return new Font(fontNames[index], style, size);
}
/*
*生成随机颜色方法
*/
public Color randomColor() {
int red = r.nextInt(150);
int green = r.nextInt(150);
int blue = r.nextInt(150);
return new Color(red, green, blue);
}
/*
*生成干扰线方法
*/
public void drawLine(BufferedImage image) {
//循环三次,画三条干扰线
//先取到画笔,然后才能画线嘛!
Graphics graphics = image.getGraphics();
for (int i = 0; i < 3; i++) {
int x1 = r.nextInt(width);
int y1 = r.nextInt(height);
int x2 = r.nextInt(width);
int y2 = r.nextInt(height);//得到所要划线的起点和终点坐标
graphics.setColor(Color.BLUE);//设置干扰线颜色
graphics.drawLine(x1, y1, x2, y2);
}
}
} |
package servlet;
import util.VerifyCode;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.IOException;
public class VerifyCodeServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
VerifyCode vc = new VerifyCode();
BufferedImage image = vc.getImage();
request.getSession().setAttribute("session_vcode", vc.getText());
VerifyCode.output(image, response.getOutputStream());
}
} |
dao.UserDao=dao.JdbcUserDaoImpl |
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mydb?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8
username=root
password=liulei2021 |
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>RegistServlet</servlet-name>
<servlet-class>servlet.RegistServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>VerifyCodeServlet</servlet-name>
<servlet-class>servlet.VerifyCodeServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>LoginServlet</servlet-name>
<servlet-class>servlet.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>RegistServlet</servlet-name>
<url-pattern>/RegistServlet</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>VerifyCodeServlet</servlet-name>
<url-pattern>/VerifyCedeServlet</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>LoginServlet</servlet-name>
<url-pattern>/LoginServlet</url-pattern>
</servlet-mapping>
</web-app> |
<%-- |
<%--
Created by IntelliJ IDEA.
User: Liu'lei
Date: 2021/11/5
Time: 17:22
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>登录页</title>
</head>
<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}"/>${errors.username}<br/>
密 码:<input type="password" name="password" value="${user.password}"/>${errors.password}<br/>
<input type="submit" value="登录"><br/><br/>
<a href="/user/regist.jsp">没有账号?点击去注册!</a>
</form>
</body>
</html> |
<%--
Created by IntelliJ IDEA.
User: Liu'lei
Date: 2021/11/5
Time: 17:22
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>注册页</title>
<script type="text/javascript">
function _change() {
var ele=document.getElementById("vCode");
ele.src="<c:url value='/VerifyCedeServlet'/>?xxx="+new Date().getTime();
}
</script>
</head>
<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}"/>${errors.username}<br/>
密 码:<input type="password" name="password" value="${user.password}"/>${errors.password}<br/>
性 别:<input type="radio" name="gender" value="男" checked="checked"/>${errors.gender}男 <input type="radio" name="gender" value="女" />${errors.gender}女<br/>
年 龄:<input type="number" name="age" value="${user.age}"/>${errors.age}<br/>
验证码:<input type="text" name="verifyCode" value="${user.verifyCode}" size="10"/>${errors.verifyCode}
<img id="vCode" src="<c:url value='/VerifyCedeServlet'/> ">
<a href="javascript:_change()"> 点击更换 </a><br/>
<input type="submit" value="提交"><br/>
</form>
</body>
</html> |
<%--
Created by IntelliJ IDEA.
User: Liu'lei
Date: 2021/11/5
Time: 17:19
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>首页跳转</title>
</head>
<body>
<jsp:forward page="./user/welcome.jsp"></jsp:forward>
</body>
</html> |