用户登录注册系统(基于JSP和Servlet)

准备工作

开发环境准备

  • 开发工具:eclipse 4.3
  • 技术语言:Java SE 1.7
  • 依赖控制:Maven 3.0.4
  • 服务器:Tomcat 7.0.68
  • 操作系统:Windows 10
  • 版本管理:项目源码地址

搭建一个web项目

参考文档:快速搭建web项目

项目需求

实现一个标准的用户登录注册功能,详细需求见easyPassport需求说明

开发实现

设计架构思想

设计架构图.jpg

如上图所示,由Servlet提供后台服务,由JSP完成前端的数据展示,JavaBean作为数据存储和传输的介质,并最终持久化至数据库。

模版层设计实现

创建用户信息表:

用户信息表用来保存和记录用户的信息

create table users{
id int,
userid varchar(50),
username varchar(50),
email varchar(50),
phone varchar(11),
password varchar(50),
status varchar(2),
create_datet varchar(20),
mod_datet varchar(20),
func_arr varchar(20) default '00000000000000000000'
}

对应的数据字典:

字段 数据类型 字段说明 是否可以为空 备注
id int 数据库主键ID 数据自动生成
userid varchar(50) 业务主键ID 根据业务规则生成的主键ID
username varchar(50) 用户名称 用户注册时输入的用户名
email varchar(50) 用户邮箱 用户注册时输入的邮箱
phone varchar(11) 手机号 手机号的相关功能本期暂不涉及
password varchar(50) 密码 密码保存用户密码
status varchar(2) 用户状态:0-正常;1停用
create_datet varchar(20) 用户创建时间
mod_datet varchar(20) 用户修改时间
func_arr varchar(20) 特殊状态位 用来区分特殊也属性的用户

JavaBean、JDBC和DAO

JavaBean是数据传输的介质,JDBC是Java应用连接数据库的标准实现,DAO(Data Access Objects)则提供了对原子表的增删查改等功能。三者共同组成了一个Java应用的模版层(也叫持久层)。

JavaBean的实现

public class User implements Serializable{

    private static final long serialVersionUID = 1L;

    private long id;
    private String userId;
    private String userName;
    private String email;
    private String phone;
    private String password;
    private String status;
    private String createDatet;
    private String modDatet;
    private String funcArr;

       // get、set方法省略
       ....
}

使用JDBC技术访问数据库

JDBC是Java EE标准规范的一部分,是Java应用操作数据库时最重要的方法(甚至可以说是唯一的方法,ODBC/OCI等一堆东西除非极端情况,已经基本没人使用了),基于JDBC技术衍生出很多框架,在这里就不做演示了。

一个web项目使用JDBC连接数据库的标准步骤如下:

  • 引入必要的jar包
<!-- 省略pom.xml文件中的其他内容 -->
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <version>1.4.191</version>
</dependency>
  • 编写JDBC程序
//引入必要的包
import java.sql.*;

public class JDBCDemo {

    // JDBC 驱动名及数据库 URL(以H2为例)
    static final String JDBC_DRIVER = "org.h2.Driver";  
    static final String DB_URL = "jdbc:h2:tcp://localhost/~/test";

    // 数据库的用户名与密码,需要根据自己的设置
    static final String USER = "sa";
    static final String PASS = "sa";

    public static void main(String[] args) {
        Connection conn = null;
        Statement stmt = null;
        try{
            // 注册 JDBC 驱动
            Class.forName(JDBC_DRIVER);
        
            // 打开链接
            System.out.println("连接数据库...");
            conn = DriverManager.getConnection(DB_URL,USER,PASS);
        
            // 执行查询
            System.out.println(" 实例化Statement对...");
            stmt = conn.createStatement();
            String sql;
            sql = "SELECT * FROM USERS";
            ResultSet rs = stmt.executeQuery(sql);
        
            // 展开结果集数据库
            while(rs.next()){
                // 通过字段检索
                int id  = rs.getInt("id");
                String name = rs.getString("username");
    
                // 输出数据
                System.out.print("ID: " + id);
                System.out.print(", 用户名称: " + name);
                System.out.print("\n");
            }
            // 完成后关闭
            rs.close();
            stmt.close();
            conn.close();
        }catch(SQLException se){
            // 处理 JDBC 错误
            se.printStackTrace();
        }catch(Exception e){
            // 处理 Class.forName 错误
            e.printStackTrace();
        }finally{
            // 关闭资源
            try{
                if(stmt!=null) stmt.close();
            }catch(SQLException se2){
            }// 什么都不做
            try{
                if(conn!=null) conn.close();
            }catch(SQLException se){
                se.printStackTrace();
            }
        }
        System.out.println("Goodbye!");
    }
}

使用DAO提供基础功能

DAO通常有两部分组成,一个是通用的接口,提供标准的服务;一个是对应接口的实现类,方便扩展和更新。

接口类的设计如下:

import java.util.List;
import java.util.Map;

import javabean.User;

public interface UserDao {

    // 用户信息查询
    public User findById(long id);
    public User findOne(User user);
    public User findOne(Map<String, Object> paramMap);
    public List<User> findList(User user);
    public List<User> findList(Map<String, Object> paramMap);
    public Map<String, Object> findMap(User user);
    public Map<String, Object> findMap(Map<String, Object> paramMap);
    
    // 用户信息新增
    public int save(User user);
    public int save(Map<String, Object> paramMap);
    
    // 用户信息修改
    public int update(User user);
    public int update(Map<String, Object> paramMap);
}

DAO接口的实现类如下:


import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;

import javabean.User;

public class UserDaoImpl implements UserDao {
    
    private static final String JDBC_DRIVER = "org.h2.Driver";  
    private static final String DB_URL = "jdbc:h2:tcp://localhost/~/test";
    private static final String USER = "sa";
    private static final String PASS = "sa";
    
    private Connection conn = null;
    private static String findUserById = " select id, userid, username, email, phone, password, status, create_datet, mod_datet, func_arr from users where id = ? ";
    private static String findUsers = " select id, userid, username, email, phone, password, status, create_datet, mod_datet, func_arr from users ";

    // 创建获取数据库链接的方法
    private Connection getConnection() {
        if (null != conn) {
            return conn;
        } else {
            try {
                Class.forName(JDBC_DRIVER);
                conn = DriverManager.getConnection(DB_URL, USER, PASS);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return conn;
        }
    }
    
    @Override
    public User findById(long id) {
        if(null == conn)
            conn = this.getConnection();
        User user = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            ps = conn.prepareStatement(findUserById);
            ps.setLong(1, id);
            rs = ps.executeQuery();
            while(rs.next()){
                user = new User(
                        rs.getLong(1),      // id
                        rs.getString(2),    // userid
                        rs.getString(3),    // userName
                        rs.getString(4),    // email
                        rs.getString(5),    // phone
                        rs.getString(6),    // password
                        rs.getString(7),    // status
                        rs.getString(8),    // createDatet
                        rs.getString(9),    // modDatet
                        rs.getString(10));  // funcArr
            }
            rs.close();
            ps.close();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 确保资源关闭
            try {
                if (rs != null) rs.close();
            } catch (SQLException e) {
            }           
            try {
                if (ps != null) ps.close();
            } catch (SQLException e) {
            }
        }
        return user;
    }
......

}

控制层设计实现

控制层的功能主要由Servlet实现,通过分析需求文档可以得出,要实现本系统,至少需要Servlet提供以下服务:

  • 用户信息验证
  • 用户信息查询
  • 用户信息新增
  • 用户信息修改
  • 密码重置

使用eclipse新建Servlet类

以实现“用户信息验证”服务为例,控制层实现应首先新建Servlet类。按以下步骤操作“File” -> “New” -> “Servlet”得到如下弹出框。


新建一个Servlet.png

选择包结构,输入自定义的Servlet名称,点击“Next”,输入对这个Servlet类的描述,如果有需要,可以添加初始化参数。


添加描述信息、初始化参数

点击“Next”,根据自己需要扩展Servlet类,最后点击“Finish”完成类的新建。


添加其他接口、选择需要重写的方法

此时eclipse会自动将上述配置的内容转换为web.xml里的配置,接下来我们需要做的,就是在新增的Servlet类中,实现对应的服务。

  <!-- web.xml里新增的内容 -->
  <servlet>
    <description></description>
    <display-name>UserCheckServlet</display-name>
    <servlet-name>UserCheckServlet</servlet-name>
    <servlet-class>servlet.UserCheckServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>UserCheckServlet</servlet-name>
    <url-pattern>/UserCheckServlet</url-pattern>
  </servlet-mapping>

实现业务功能

下述代码简单描述了“用户信息验证”的业务流程,即获取参数 -> 获取后台数据 -> 对比用户信息与后台保存是否一致 -> 跳转到指定页面。

/**
 * 用户信息验证
 */
public class UserCheckServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        // 默认调用doPost方法,免得同一段代码写多次
        this.doPost(request, response);
    }

    protected void doPost(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        // 获取用户输入的信息
        String userName = (String)request.getParameter("userName");
        String passWord = (String)request.getParameter("passWord");
        // 将用户名称作为参数传递给后台DAO 
        UserDao userDao = new UserDaoImpl();
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("userName", userName);
        // DAO完成用户用户信息检索
        User user = userDao.findOne(paramMap);
        // 根据查询的结果判断用户是否真实存在,并跳转到指定的页面
        if(user.getPassword().equals(passWord)){
            response.sendRedirect("登录成功跳转到首页");
        } else {
            response.sendRedirect("登陆不成功则停留在登录页面");
        }
    }
}

JSP实现前端页面展示

eclipse新建一个JSP文件时会有很多问题,比如默认不支持中文,要解决这类问题就要设置一下eclipse的默认配置。

按照以下步骤“Windows -> “Preferences” -> “Web” -> “JSP File” -> “Editor” -> “Templates”,找到对应的页面。


新建JSP文件

点击“Editor”编辑JSP文件模版,这样再次新建文件时就不会有问题了。

<%@ page language="java" contentType="text/html; charset=UTF-8"
    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="<%=basePath%>">   
    <title>标题</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">
 </head>
 <body>
 ${cursor}
 </body>
</html>

总结

至此,我们已经基本实现了用户登录注册系统涉及的功能(项目源码地址)。但一个完善的,工业级别的系统远非仅仅“实现功能”这么简单,以本项目为例,应至少满足以下几点要求才能最终上线。

  1. 系统的性能要足够好
  2. 系统的安全性要有保障

性能是衡量一个软件产品的终极指标,世界上任何一款软件产品都会在满足所以业务功能基础上,尽力提升性能,提高程序的运转效率。JSP和Servlet技术是Java Web技术的基础,它有着上手简单,文档资源丰富等优点,可以让稍有Java基础的同学在搜索引擎的帮助下,快速实现功能。但也正是因为它“太基础”,很多性能相关的内容都需要程序员自己设计实现,而这些内容,恰恰是网上“稀有资源”。

web应用通常会暴露在公网环境中,安全性也不容忽视。安全问题是相对的,不存在“绝对安全”一说,但一个需要正式上线运行的产品一定要有对应的安全规格和标准。这么做既是对产品负责,更是对用户负责。

所以,这个“登录注册系统”下一阶段的目标,就是提升性能表现,提高安全等级。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,029评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,395评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,570评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,535评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,650评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,850评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,006评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,747评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,207评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,536评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,683评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,342评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,964评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,772评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,004评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,401评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,566评论 2 349

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,598评论 18 399
  • 一. Java基础部分.................................................
    wy_sure阅读 3,805评论 0 11
  • 复习复习!!!搞起来!!Servlet和JSP是Java EE规范最基本成员,他们是Java Web开发的重点知识...
    JackFrost_fuzhu阅读 2,832评论 1 71
  • 前一阵子公司电影节办影展,有一场让我印象深刻。当时放的是一个新晋导演的处女座,讲九十年代深圳移民的故事。镜头跟随一...
    司昀阅读 647评论 0 2
  • 小李掐指一算不少小秘书都放暑假了,暑假宅在家最适合煲剧了... 于是,贴心的给小秘书们送上美剧——《妙警贼探》 基...
    0773e17b1899阅读 1,239评论 0 1