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.将&换为&
-->
<property name="jdbcUrl">jdbc:mysql://127.0.0.1:3306/huawei?autoReconnect=true&characterEncoding=UTF-8&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>©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";
});
});
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 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的设计差异
· 三行代码完成国际化适配,妙~啊~