Servlet-登录实战

准备

1.导入相关包

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.huawei</groupId>
    <artifactId>JavaServletDemo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <name>JavaServletDemo</name>
    <packaging>war</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.target>1.8</maven.compiler.target>
        <maven.compiler.source>1.8</maven.compiler.source>
        <junit.version>5.8.1</junit.version>
    </properties>

    <!-- maven依赖配置 -->
    <dependencies>
        <!-- Servlet核心包 -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
            <scope>provided</scope>
        </dependency>

        <!-- Fastjoson JSON处理工具 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.78</version>
        </dependency>

        <!-- MySQLJDBC驱动包 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.22</version>
        </dependency>
        <!-- C3P0连接池 -->
        <dependency>
            <groupId>com.mchange</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.5.5</version>
        </dependency>
        <!-- C3P0依赖包:mchange-commons-java -->
        <dependency>
            <groupId>com.mchange</groupId>
            <artifactId>mchange-commons-java</artifactId>
            <version>0.2.20</version>
        </dependency>


        <!-- 单元测试工具 -->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <!-- maven构建项目配置 -->
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>3.3.2</version>
            </plugin>
        </plugins>
    </build>
</project>

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"?>
<c3p0-config>
    <!-- 默认配置 -->
    <default-config>
        <!-- 驱动 -->
        <property name="driverClass">com.mysql.cj.jdbc.Driver</property>
        <!-- jdbcUrl -->
        <!-- 1.将url使用转义标签<![CDATA[JDBCUrl]]>
             2.将&换为&amp;
        -->
        <property name="jdbcUrl">jdbc:mysql://127.0.0.1:3306/huawei?autoReconnect=true&amp;characterEncoding=UTF-8&amp;serverTimezone=UTC</property>
        <!-- 用户名 -->
        <property name="user">root</property>
        <!-- 密码 -->
        <property name="password">root123456</property>
    </default-config>
</c3p0-config>

工具类:

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;
%>
<!-- layui相关类库 -->
<link type="text/css" rel="stylesheet" href="<%=basePath%>/lib/layui/css/layui.css" />
<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>
<!-- cookie类库 -->
<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

<!-- Java图形验证码 -->
        <dependency>
            <groupId>com.github.whvcse</groupId>
            <artifactId>easy-captcha</artifactId>
            <version>1.6.2</version>
        </dependency>

(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等等)
<!DOCTYPE html>
<html>
    。。。。 html内容
</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) 前端-验证码刷新操作

<img id="regCodeImg" src="http://localhost:8080/JavaServletDemo/regImg" onclick="reshRegCode()"/>

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代码

<input type="checkbox" name="remenberStaff" title="记住我" lay-skin="primary" />

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"%>
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>学生管理首页</title>
    <link type="text/css" rel="stylesheet" href="<%=basePath%>/views/sys/css/index.css" />
    <%@ include file="js/index.js.jsp"%>
</head>
<body>
<div class="left_div">
    <div class="title_div">
        <h3>学生管理系统</h3>
    </div>
    <ul id="myMenu" class="layui-nav layui-nav-tree" style="border-radius: 0px;height: calc(100% - 60px);"
        lay-filter="myMenu">

        <!-- <li class="layui-nav-item layui-nav-itemed">
            <a href="javascript:;">系统管理</a>
            <dl class="layui-nav-child">
                <dd><a data-urlPath="index.html">选项1</a></dd>
                <dd><a data-urlPath="index2.html">选项2</a></dd>
            </dl>
        </li> -->
    </ul>
</div>
<div class="right_div">
    <div class="top_div">
        <ul class="layui-nav">
            <li class="layui-nav-item">
                <a href=""><img style="width:50px;border-radius: 50%;" src="<%=basePath%>/views/static/tx1.jpeg" layui-nav-img ">
                    #!!!!!!      登录人用户名渲染     !!!!!!
                    <span><%=session.getAttribute("loginUserName")%></span></a>
                <dl class=" layui-nav-child">
                    <dd><a href="javascript:;">修改信息</a></dd>
                    <dd><a href="javascript:;">安全管理</a></dd>
                    <dd onclick="outLogin()"><a href="javascript:;">退了</a></dd>
                </dl>
            </li>
            <li class="layui-nav-item">
                <a href="">个人中心<span class="layui-badge-dot"></span></a>
            </li>
            <li class="layui-nav-item">
                <a href="">控制台<span class="layui-badge">9</span></a>
            </li>


        </ul>
    </div>

    <div class="main_div">
        <iframe id="mainFrame" src="" name="mainFrame" style="border:0px;width:100%;height:100%;"></iframe>
    </div>
    <div class="bottom_div">
        <span>&copy;2019 Zheng Jie Apache License 2.0 ⋅ 浙ICP备18005431号-6</span>
    </div>
</div>
</body>



</html>


(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:
<dd onclick="outLogin()"><a href="javascript:;">退了</a></dd>
    
   
js:
function outLogin(){
	layui.use(['element'],function(){
		
		layer.msg('即将退出!正在清除缓存 ... ...', {
		  icon: 1,
		  shade: [0.8, '#393D49'],
		  time: 1000
		}, function(){
		  
		  location.href = "<%=basePath%>/loginout";
		  
		});   
		
	});
}
posted @   忙碌的高师傅  阅读(201)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~
点击右上角即可分享
微信分享提示