Servlet-登录实战


准备

1.导入相关包

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

    4.0.0

    com.huawei
    JavaServletDemo
    1.0-SNAPSHOT
    JavaServletDemo
    war

    
        UTF-8
        1.8
        1.8
        5.8.1
    

    
    
        
        
            javax.servlet
            javax.servlet-api
            4.0.1
            provided
        

        
        
            com.alibaba
            fastjson
            1.2.78
        

        
        
            mysql
            mysql-connector-java
            8.0.22
        
        
        
            com.mchange
            c3p0
            0.9.5.5
        
        
        
            com.mchange
            mchange-commons-java
            0.2.20
        


        
        
            org.junit.jupiter
            junit-jupiter-api
            ${junit.version}
            test
        
        
            org.junit.jupiter
            junit-jupiter-engine
            ${junit.version}
            test
        
    

    
    
        
            
                org.apache.maven.plugins
                maven-war-plugin
                3.3.2
            
        
    

2.响应实体的封装

/**
 * 创建日期:2021/12/22
 * 作者:高靖博
 * 公司:华为(昆明)数字经济学院
 */
public class Result implements Serializable {

    private String code;//成功代码

    private String msg;//返回的消息(操作成功!  系统异常!  权限不足)

    private Object data = new Object();//封装返回数据的对象   {}

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    @Override
    public String toString() {
        return JSON.toJSONString(this);
    }
}

3.设计好分层架构

如:

sys

? controller =》 Servlet代码(用于处理请求和响应)

? dto =》 存放service层与controller层数据交换的对象实体

? util =》工具类(C3P0连接池工具)

? service =》 业务层接口类 (为了能够实现在控制器层调用业务代码解耦设计)

? impl =》 业务层接口实现类

4.JDBC连接池工具类

配置文件:resource目录

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

    
    
        
        com.mysql.cj.jdbc.Driver
        
        
        jdbc:mysql://127.0.0.1:3306/huawei?autoReconnect=true&characterEncoding=UTF-8&serverTimezone=UTC
        
        root
        
        root123456
    

工具类:

package com.huawei.javaservletdemo.sys.util;

import com.mchange.v2.c3p0.ComboPooledDataSource;

import java.sql.Connection;
import java.sql.SQLException;

/**
 * 创建日期:2021/12/23
 * 作者:高靖博
 * 公司:华为(昆明)数字经济学院
 */
public class MyC3P0Util {

    //数据源对象
    private static ComboPooledDataSource cpd;

    static{
        cpd = new ComboPooledDataSource();
    }

    /**
     * 获取连接池连接
     * @return
     */
    public static Connection getCon(){
        Connection connection = null;
        try {
            connection = cpd.getConnection();
        } catch (SQLException e) {
            System.err.println("C3P0连接池-获取连接异常");
            e.printStackTrace();
        }
        return connection;
    }

}

5.编写前端公共引入的jsp文件,common.jsp

<%-- basePath路径 --%>
<%
    String scheme = request.getScheme();//协议类型
    String serverName = request.getServerName();//服务地址
    int serverPort = request.getServerPort();//服务端口
    String contextPath = request.getContextPath();//项目名称

    String basePath = scheme+"://"+serverName+":"+serverPort+contextPath;
%>


<script type="text/javascript" src="<%=basePath%>/lib/layui/layui.js"></script>
<script type="text/javascript" src="<%=basePath%>/lib/jquery-3.6.0-min.js"></script>

<script src="//cdn.bootcss.com/jquery-cookie/1.4.1/jquery.cookie.min.js"></script>

在需要引入公共文件的页面头部引入jsp

<%@ include file="../common/common.jsp"%>

1.登录Servlet编写

/**
     * 相关的数据接口
     */
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        Result res = new Result();

        req.setCharacterEncoding("UTF-8");
        resp.setCharacterEncoding("UTF-8");

        String userName = req.getParameter("userName");
        String pwd = req.getParameter("pwd");

        //判断用户是否存在-》业务层
        try {
            UserDto userDto = loginService.doLogin(userName, pwd);

            res.setData(userDto);

            if(userDto!=null){
                res.setCode("0000");
                res.setMsg("登录成功!");
            }else{
                res.setCode("0000");
                res.setMsg("登录失败!");
            }

        } catch (SQLException e) {
            e.printStackTrace();
            res.setCode("9999");
            res.setMsg("系统异常!");
        }

        String s = res.toString();
        System.out.println(s);

        resp.setContentType("application/json;charset=UTF-8");
        PrintWriter writer = resp.getWriter();
        writer.print(s);
        writer.flush();
        writer.close();


    }

2.Service接口定义

package com.huawei.javaservletdemo.sys.service;

import com.huawei.javaservletdemo.sys.dto.UserDto;

import java.sql.SQLException;

/**
 * 登录接口
 * 创建日期:2021/12/23
 * 作者:高靖博
 * 公司:华为(昆明)数字经济学院
 */
public interface LoginService {

    UserDto doLogin(String userName, String pwd) throws SQLException;

}

3.Service实现层实现

package com.huawei.javaservletdemo.sys.service.impl;

import com.huawei.javaservletdemo.sys.dto.UserDto;
import com.huawei.javaservletdemo.sys.service.LoginService;
import com.huawei.javaservletdemo.sys.util.MyC3P0Util;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * 创建日期:2021/12/23
 * 作者:高靖博
 * 公司:华为(昆明)数字经济学院
 */
public class LoginServiceImpl implements LoginService {

    /**
     * 验证登录
     * @param userName 用户名
     * @param pwd 密码
     * @return 返回登录用户实体
     */
    @Override
    public UserDto doLogin(String userName, String pwd) throws SQLException {

        //1.JDBC比较
        Connection con = MyC3P0Util.getCon();

        String sql = "select * from t_user where userName=? and pwd=MD5(?)";

        PreparedStatement ps = con.prepareStatement(sql);

        ps.setString(1,userName);
        ps.setString(2,pwd);

        ResultSet resultSet = ps.executeQuery();

        if(resultSet.next()){

            UserDto user = new UserDto();

            user.setUserName(resultSet.getString("userName"));
            return user;
        }

        return null;
    }

}

4.测试接口,使用POSTMAN

5.验证码的实现

(1)引入包 easy-captcha


        
            com.github.whvcse
            easy-captcha
            1.6.2
        

(2) 编写请求验证码的Servlet接口

package com.huawei.javaservletdemo.sys.controller;

import com.wf.captcha.SpecCaptcha;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

/**
 * 创建日期:2021/12/23
 * 作者:高靖博
 * 公司:华为(昆明)数字经济学院
 */
@WebServlet("/regImg")
public class RegCodeServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        req.setCharacterEncoding("UTF-8");
        resp.setCharacterEncoding("UTF-8");

        //创建验证码图形对象,设置宽度、高度(PX)  默认创建英文字符和数字的静态验证码
        SpecCaptcha sc = new SpecCaptcha(130,48);

        //验证码的文字
        String text = sc.text();

        //保存到Session
        HttpSession session = req.getSession();

        session.setMaxInactiveInterval(60*15);

        session.setAttribute("regCode",text);

        System.out.println("验证码:"+text);
        sc.out(resp.getOutputStream());


    }
}

(3) 在登录操作时,取出验证码进行校验

 HttpSession session = req.getSession();//获取session

        if(session.getAttribute("regCode")!=null){
            String s_regCode = (String) session.getAttribute("regCode");
            if(!s_regCode.equalsIgnoreCase(regCode)){//比较两个字符串忽略大小写
                res.setCode("0001");
                res.setMsg("验证码错误!");
                res.setData(new Object());

                String s = res.toString();
                System.out.println(s);

                writer.print(s);
                writer.flush();

                return;

            }

        }else{
            res.setCode("0002");
            res.setMsg("验证码过期,请从新刷新验证码!");
            res.setData(new Object());

            String s = res.toString();
            System.out.println(s);

            writer.print(s);
            writer.flush();

            return;
        }

(4)前端登录部分代码

1.我们将html页面移入jsp页面,引入公共文件common.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ include file="../common/common.jsp"%>      #根据业务引入公共文件(layui、JQuery、css等等)


    。。。。 html内容

2.将js文件改为XXX.js.jsp,实现jsp模板套用

<%@ page contentType="text/html;charset=UTF-8" %>
<script type="text/javascript">
    。。。。。  js代码
</script>

前端登录接口调用ajax

$.ajax({
				url:"<%=basePath%>/login",
				type:"POST",
				data:{userName:s_username,pwd:s_pwd,regCode:s_regCode,isRem:s_rem},
				dataType:"JSON",
				beforeSend:function(){
					loadindex = layer.load(2,{shade: [0.8, '#393D49']});
				},
				success:function(data){

					layer.close(loadindex);

					if(data.code=="0000"){
						layer.msg('登录成功!正在进入... ...', {
								  icon: 1,
								  shade: [0.8, '#393D49'],
								  time: 1000
								}, function(){
									//访问请求转发至主页页面的servlet
								  location.href = "<%=basePath%>/main";

					    });
					}else if(data.code=="0002"){
						layer.msg(data.msg, {
							icon: 1,
							shade: [0.8, '#393D49'],
							time: 500
						}, function(){

							//验证码过期,从新进入登录页面
							location.href = "<%=basePath%>/login";

						});
					}else if(data.code=="0001"){
                        layer.msg(data.msg, {icon: 5});
                    }else{
							errTimes--;
								if(errTimes>0){
									layer.msg('用户名或密码错误!还剩【'+errTimes+'】次登录尝试次数!', {icon: 5});
									return false;
								}

								//锁定操作(登录按钮样式改变)layui-btn-disabled disable
								$("#loginBut").attr("class","layui-btn layui-btn-disabled layui-btn-fluid");
								$("#loginBut").attr("disabled","disabled");

								//设置一个计时器,在60s之后解锁
								//右上角弹出一个提示锁定时间  overTimeDiv
								var index = layer.open({
									title:"错误提示",
									type:1,
									offset: 'rt',
									shade:0,
									anim: 1,
									content: $("#overTimeDiv")
								});

								var len = 3;
								var timeObj = setInterval(function(){

									$("#overTime").html(len);

									len--;

									if(len==0){
										clearInterval(timeObj);
									}

								},1000);


								setTimeout(function(){
									errTimes = 3;
									$("#loginBut").attr("class","layui-btn layui-btn-normal layui-btn-fluid");
									$("#loginBut").attr("disabled",false);
									layer.close(index);

									$("#overTimeDiv").css("display","none");

								},3000);
					}

				},
				error:function(XMLHttpRequest, textStatus, errorThrown){
					console.dir("请求失败!");
				}
			});

(5) 前端-验证码刷新操作



function reshRegCode(){
	console.dir($("#regCodeImg"));
	$("#regCodeImg").attr("src","http://localhost:8080/JavaServletDemo/regImg?time="+new Date().getTime());

}

6.前端登录时,“记住我”功能实现

实现的原理:

(1)请求时发送一个数据告诉服务器我要设置Cookie

(2)服务器接受到响应后给客户端发送设置一个Cookie

1.设置Cookie

html代码



js代码

//在登录时发送一个告诉服务器是否要设置Cookie的选线
form.on('submit(doLogin)',function(obj){
			
			var sc_users = obj.field;

			//告诉服务器是否要记住用户,设置Cookie
			var s_rem = false;
			if(sc_users.remenberStaff!=undefined){//要设置
				s_rem = true;
			}
    .....忽略
    
    $.ajax({
				url:"http://localhost:8080/JavaServletDemo/login",
				type:"POST",
				data:{userName:s_username,pwd:s_pwd,regCode:s_regCode,isRem:s_rem},
        
        .....忽略

后端Servlet代码

//String -> boolean
        boolean isRem = Boolean.valueOf(req.getParameter("isRem"));

        //是否添加客户端cookie
        if(isRem){

            Cookie userName_cookie = new Cookie("login_userName",userName);
            Cookie userPwd_cookie = new Cookie("login_pwd",pwd);

            //将Cookie放入响应对象中
            resp.addCookie(userName_cookie);
            resp.addCookie(userPwd_cookie);

        }

2.前端读取Cookie

(1) 在前端读取Cookie,JQuery读取Cookie的方法,引入一个JQuery的Cookie操作包jquery.cookie.min.js
<script src="//cdn.bootcss.com/jquery-cookie/1.4.1/jquery.cookie.min.js"></script>
(2)读取Cookie,赋值表单
//初始化第一步加载Cookie回填登录表单
	var cookie_userPwd = $.cookie("login_pwd");//获取对应Name的Cookie值
	var cookie_userName = $.cookie("login_userName");

	$("input[name='userName']").val(cookie_userName);//表单赋值
	$("input[name='password']").val(cookie_userPwd);

7.实现登录功能

(1) 当登录成功后,保存登录人的信息到Session会话中

if(userDto!=null){
                res.setCode("0000");
                res.setMsg("登录成功!");

                HttpSession loginSession = req.getSession();

                loginSession.setAttribute("loginUserName",userName);

(2)登录成功,跳转到主页的Servlet, 特殊操作,非法请求首页servlet的拦截

package com.huawei.javaservletdemo.sys.controller;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

/**
 * 创建日期:2021/12/27
 * 作者:高靖博
 * 公司:华为(昆明)数字经济学院
 */
@WebServlet("/main")
public class ToMainServlet extends HttpServlet {

    /**
     * 跳转到首页页面
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        req.setCharacterEncoding("UTF-8");
        resp.setCharacterEncoding("UTF-8");

        //从session中获取登录的用户名
        HttpSession session = req.getSession();

        if(session.getAttribute("loginUserName")!=null){//用户会话已存在,说明已登录
            String pathHead = (String) getServletContext().getAttribute("pathHead");

            req.getRequestDispatcher("views/sys/main.jsp").forward(req,resp);
        }else{//未登录过没有会话则直接到登录页面
            resp.sendRedirect("views/sys/login.jsp");
        }
    }
}

(3)前端部分:主页代码,包含Session动态渲染用户名

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ include file="../common/common.jsp"%>



    
    学生管理首页
    
    <%@ include file="js/index.js.jsp"%>


学生管理系统

(4)退出操作

后端:

package com.huawei.javaservletdemo.sys.controller;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

/**
 * 创建日期:2021/12/27
 * 作者:高靖博
 * 公司:华为(昆明)数字经济学院
 */
@WebServlet("/loginout")
public class LoginOutServlet extends HttpServlet {

    /**
     * 退出登录方法
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        req.setCharacterEncoding("UTF-8");
        resp.setCharacterEncoding("UTF-8");

        //退出登录第一步,移除登录会话信息
        HttpSession session = req.getSession();

        session.removeAttribute("loginUserName");

        resp.sendRedirect("views/sys/login.jsp");

    }
}

前端:

html:
退了
js: function outLogin(){ layui.use(['element'],function(){ layer.msg('即将退出!正在清除缓存 ... ...', { icon: 1, shade: [0.8, '#393D49'], time: 1000 }, function(){ location.href = "<%=basePath%>/loginout"; }); }); }