网站3天免输入登录页面编程
掌握JSP的内置对象技术以及表单验证在服务器端的实现技术编写网站的登录页面,要求能保存用户登录成功时输入的用户名及密码信息,当同一用户 3天内再次登录网站首页时,能自动读取之前保存的用户信息判断身份是否合法,如果合法自动完成登录。如果当同一用户超过3天再次登录网站首页时,则需要重新输入用户名及密码进行身份验证,通过验证方能登录并显示欢迎信息。否则提示:身份验证失败,必须重新输入。操作系统:Micr
一、实验目的
掌握JSP的内置对象技术以及表单验证在服务器端的实现技术
二、实验内容与设计思想
编写网站的登录页面,要求能保存用户登录成功时输入的用户名及密码信息,当同一用户 3天内再次登录网站首页时,能自动读取之前保存的用户信息判断身份是否合法,如果合法自动完成登录。如果当同一用户超过3天再次登录网站首页时,则需要重新输入用户名及密码进行身份验证,通过验证方能登录并显示欢迎信息。否则提示:身份验证失败,必须重新输入。
三、 实验使用环境
操作系统: Microsoft Windows 10
编程环境:JDK1.8、TOMCAT 8.0 、DreamWeaver CS5或Eclipse Developer for JavaEE
四、 核心代码实现
1. index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="java.net.URLDecoder" %>
<html>
<head><title>首页</title></head>
<body>
<%
// 获取所有 Cookie
Cookie[] cookies = request.getCookies();
String username = null;
String password = null;
long loginTime = 0;
if (cookies != null) {
for (Cookie cookie : cookies) {
if ("user".equals(cookie.getName())) {
// Cookie 格式:username:password:timestamp
String[] parts = cookie.getValue().split(":");
if (parts.length == 3) {
username = URLDecoder.decode(parts[0], "UTF-8");
password = URLDecoder.decode(parts[1], "UTF-8");
loginTime = Long.parseLong(parts[2]);
}
}
}
}
// 如果存在 Cookie 且未过期(3天内),自动登录
if (username != null && password != null) {
long currentTime = System.currentTimeMillis();
long threeDaysInMillis = 3 * 24 * 60 * 60 * 1000; // 3天
if ((currentTime - loginTime) <= threeDaysInMillis) {
// 自动登录成功,跳转到欢迎页
response.sendRedirect("welcome.jsp?username=" + java.net.URLEncoder.encode(username, "UTF-8"));
} else {
// 超过3天,清除旧 Cookie,显示登录页
Cookie clearCookie = new Cookie("user", "");
clearCookie.setMaxAge(0); // 删除 Cookie
response.addCookie(clearCookie);
request.getRequestDispatcher("login.jsp").forward(request, response);
}
} else {
// 没有 Cookie,显示登录页
request.getRequestDispatcher("login.jsp").forward(request, response);
}
%>
</body>
</html>
2. login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>安全登录中心</title>
<!-- 引入 Bootstrap 5 CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- 引入图标库 -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css" rel="stylesheet">
<style>
/* 1. 高级感渐变背景 */
body {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
font-family: "Microsoft YaHei", sans-serif;
}
/* 2. 卡片玻璃拟态效果(可选,这里用纯白更清晰,加一点阴影) */
.login-card {
border: none;
border-radius: 20px;
box-shadow: 0 15px 35px rgba(0, 0, 0, 0.1);
overflow: hidden; /* 防止内容溢出圆角 */
background: #ffffff;
}
/* 3. 标题装饰 */
.card-header {
background: #fff;
border-bottom: 1px solid #f0f0f0;
padding: 2rem 2rem 1rem;
text-align: center;
}
.card-header h3 {
font-weight: 700;
color: #333;
margin: 0;
letter-spacing: 1px;
}
.card-body {
padding: 2rem;
}
/* 4. 按钮渐变与动画 */
.btn-primary {
background: linear-gradient(to right, #667eea, #764ba2);
border: none;
padding: 10px 0;
font-weight: 600;
letter-spacing: 1px;
transition: all 0.3s ease;
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
}
/* 5. 输入框样式微调 */
.form-control:focus {
border-color: #667eea;
box-shadow: 0 0 0 0.2rem rgba(102, 126, 234, 0.25);
}
/* 6. 提示框美化 */
.alert-custom {
border-radius: 10px;
font-size: 0.9rem;
animation: slideDown 0.5s ease-out;
}
@keyframes slideDown {
from { opacity: 0; transform: translateY(-10px); }
to { opacity: 1; transform: translateY(0); }
}
</style>
</head>
<body>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-4 col-lg-4">
<div class="card login-card">
<!-- 卡片头部:标题 -->
<div class="card-header">
<h3><i class="bi bi-shield-lock-fill text-primary"></i> 安全登录</h3>
</div>
<div class="card-body">
<!-- 7. 动态消息提示区域 -->
<%
// 获取后端传来的错误消息或提示
String errorMsg = (String) request.getAttribute("errorMsg");
String successMsg = (String) request.getAttribute("successMsg"); // 假设你可能会有成功提示
%>
<!-- 如果有错误消息,显示红色警告 -->
<% if (errorMsg != null) { %>
<div class="alert alert-danger alert-dismissible fade show alert-custom" role="alert">
<i class="bi bi-exclamation-triangle-fill"></i>
<%= errorMsg %>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
<% } %>
<!-- 如果有成功消息(例如注册成功),显示绿色/蓝色提示 -->
<% if (successMsg != null) { %>
<div class="alert alert-success alert-dismissible fade show alert-custom" role="alert">
<i class="bi bi-check-circle-fill"></i>
<%= successMsg %>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
<% } %>
<!-- 8. 登录表单 -->
<form action="LoginServlet" method="post">
<!-- 用户名输入 -->
<div class="form-floating mb-3">
<input type="text" class="form-control" id="username" name="username" placeholder="请输入用户名" required>
<label for="username"><i class="bi bi-person"></i> 用户名</label>
</div>
<!-- 密码输入 -->
<div class="form-floating mb-4">
<input type="password" class="form-control" id="password" name="password" placeholder="请输入密码" required>
<label for="password"><i class="bi bi-key"></i> 密码</label>
</div>
<!-- 登录按钮 -->
<div class="d-grid">
<button type="submit" class="btn btn-primary btn-lg">
<i class="bi bi-box-arrow-in-right"></i> 立即登录
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<!-- 引入 Bootstrap JS (用于关闭提示框等交互) -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
3. LoginServlet.java
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.net.URLEncoder;
import javax.servlet.annotation.WebServlet;
@WebServlet("/LoginServlet")
public class LoginServlet extends HttpServlet {
/**
* 新增:处理 GET 请求
* 当用户直接在浏览器地址栏输入 URL 时,会自动跳转到登录页,避免 405 错误。
*/
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 重定向回登录页面
response.sendRedirect("login.jsp");
}
/**
* 处理 POST 请求(表单提交)
* 逻辑:记住第一次输入的密码,第二次进行比对。
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 设置编码
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
// 获取表单数据
String username = request.getParameter("username");
String password = request.getParameter("password");
// 获取 Session 对象
HttpSession session = request.getSession();
// 从 Session 中获取之前保存的数据
String registeredPassword = (String) session.getAttribute("registeredPassword");
String sessionUsername = (String) session.getAttribute("username");
// --- 核心修改:检测是否切换了账号 ---
// 如果 Session 里有用户名,但和当前输入的用户名不一样,说明换人了
if (sessionUsername != null && !sessionUsername.equals(username)) {
// 强制重置 Session,清除旧账号的密码,让新账号重新开始
session.removeAttribute("registeredPassword");
session.removeAttribute("username");
// 更新标记,避免后续逻辑混乱
registeredPassword = null;
}
// 核心逻辑:判断是“第一次输入(注册)”还是“第二次输入(登录)”
if (registeredPassword == null) {
// --- 情况 1:第一次输入(注册) ---
// 此时 Session 里没有密码,我们将当前输入的密码存入 Session
// 1. 保存密码到 Session
session.setAttribute("registeredPassword", password);
// 2. 保存用户名到 Session
session.setAttribute("username", username);
// 3. 提示用户注册成功,请再次输入
request.setAttribute("errorMsg", "注册成功!请在下方再次输入密码以完成登录。");
request.getRequestDispatcher("login.jsp").forward(request, response);
} else {
// --- 情况 2:第二次输入(登录) ---
// 此时 Session 里已经有密码了,我们需要进行比对
if (password.equals(registeredPassword)) {
// 密码比对一致,登录成功
// 1. 获取当前时间戳
long currentTime = System.currentTimeMillis();
// 2. 创建 Cookie
String cookieValue = URLEncoder.encode(username, "UTF-8") + ":" +
URLEncoder.encode(password, "UTF-8") + ":" +
currentTime;
Cookie userCookie = new Cookie("user", cookieValue);
// 3. 设置 Cookie 有效期为 3 天
userCookie.setMaxAge(3 * 24 * 60 * 60);
// 4. 设置路径
userCookie.setPath("/");
// 5. 添加 Cookie
response.addCookie(userCookie);
// 6. 跳转
response.sendRedirect("welcome.jsp?username=" + URLEncoder.encode(username, "UTF-8"));
} else {
// 密码不一致,登录失败
request.setAttribute("errorMsg", "密码与注册时不一致,登录失败。");
request.getRequestDispatcher("login.jsp").forward(request, response);
}
}
}
}
4.welcome.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
// 保持原有的逻辑判断:如果没有用户名,就踢回登录页
String username = request.getParameter("username");
if (username == null || username.isEmpty()) {
response.sendRedirect("login.jsp"); // 注意:根据你之前的代码,登录页应该是 login.jsp
return;
}
%>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>欢迎回来</title>
<!-- 引入 Bootstrap 5 CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- 引入图标库 (Bootstrap Icons) -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css" rel="stylesheet">
<style>
/* 保持与登录页一致的渐变背景 */
body {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
font-family: "Microsoft YaHei", sans-serif;
}
/* 卡片样式优化 */
.welcome-card {
border: none;
border-radius: 20px;
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
text-align: center;
padding: 3rem 2rem;
background: #ffffff;
max-width: 400px;
width: 100%;
animation: fadeIn 0.8s ease-out; /* 添加淡入动画 */
}
/* 图标样式 */
.avatar-icon {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
width: 80px;
height: 80px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 40px;
margin: 0 auto 1.5rem auto;
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
}
/* 用户名高亮样式 */
.user-highlight {
background: linear-gradient(120deg, #84fab0 0%, #8fd3f4 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
font-weight: 800;
font-size: 1.5rem;
}
/* 按钮样式 */
.btn-logout {
background: #ff6b6b;
border: none;
color: white;
padding: 10px 30px;
border-radius: 50px;
font-weight: 600;
transition: all 0.3s;
margin-top: 10px;
text-decoration: none;
display: inline-block;
}
.btn-logout:hover {
background: #ee5a52;
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(255, 107, 107, 0.4);
color: white;
}
/* 动画定义 */
@keyframes fadeIn {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
</style>
</head>
<body>
<div class="welcome-card">
<!-- 用户图标 -->
<div class="avatar-icon">
<i class="bi bi-person-circle"></i>
</div>
<!-- 欢迎文字 -->
<h2 class="mb-3">登录成功!</h2>
<p class="lead mb-4">
欢迎回来,<br>
<span class="user-highlight"><%= username %></span>
</p>
<hr class="my-4">
<!-- 退出按钮 -->
<!-- 注意:这里假设你有一个 logout.jsp 或者 logout Servlet -->
<a href="logout.jsp" class="btn-logout">
<i class="bi bi-box-arrow-right me-2"></i> 安全退出
</a>
</div>
</body>
</html>
5.logout.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
// 1. 获取名为 "user" 的 Cookie
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
if ("user".equals(cookie.getName())) {
// 2. 设置有效期为 0,立即删除该 Cookie
cookie.setMaxAge(0);
cookie.setPath("/");
response.addCookie(cookie);
}
}
}
// 3. 重定向回登录页
response.sendRedirect("login.jsp");
%>
五、调试过程与效果分析
在实验过程中,针对 Session 机制和 Cookie 机制进行了多轮调试,具体过程如下:
1.首次输入测试(注册逻辑)
操作:在登录页输入用户名“test”,密码“123456”,点击登录。
现象:页面未跳转,但在上方提示“注册成功!请在下方再次输入密码以完成登录。”。
分析:此时服务器端 Session 中已存入 registeredPassword = "123456",等待第二次验证。


2.密码不一致测试(验证逻辑)
操作:在第二次输入框中,故意输入错误密码“654321”。
现象:页面提示红色错误信息“密码与注册时不一致,登录失败。”。
分析:代码成功读取了 Session 中的旧密码,并与新输入进行了比对,逻辑正确。

3.登录成功与 Cookie 生成测试
操作:重新输入正确密码“123456”。
现象:页面成功跳转至 welcome.jsp,显示“登录成功!欢迎回来,test ”。

4.跨账号切换调试
问题:在 Session 未过期时,若直接切换新账号(如“nine”),系统仍要求输入旧账号的密码。
修复:在代码中增加了用户名比对逻辑。如果当前输入的用户名与 Session 中保存的用户名不一致,强制 session.invalidate()。
结果:修复后,切换账号时系统会自动重置状态,允许新账号重新进行“注册-登录”流程。


六、实验小结
通过本次小组实验,我们深入理解了 Java Web 开发中 Session 与 Cookie 的协同工作机制,主要心得如下:
1.Session 与 Cookie 的分工:Session 适合用于短期、连续的交互状态保持。本实验中利用 Session 在两次表单提交之间“记住”密码,实现了类似“二次确认”的安全机制。Cookie 适合用于长期、跨会话的状态保持。通过设置 Max-Age,实现了“3天内免登录”的功能,减轻了服务器的存储压力。
2.状态管理的陷阱与解决:实验中遇到的最大挑战是“旧 Session 干扰新请求”的问题。由于服务器端默认复用 Session,导致更换用户时出现逻辑错误。通过增加“用户名一致性校验”并在不一致时销毁 Session,解决了这一并发场景下的逻辑漏洞。这让我认识到在处理用户状态时,必须考虑到用户行为的多样性和不确定性。
3.安全性思考:虽然本实验实现了自动登录,但在实际生产中,直接在 Cookie 中明文(或简单编码)存储密码存在安全风险。更安全的做法是存储一个加密的 Token(如 JWT),服务器端仅验证 Token 的有效性而不直接暴露用户凭证。
综上所述,本次实验不仅实现了预期的登录功能,更让我们掌握了 Web 应用中用户身份识别的核心技术细节。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

所有评论(0)