Compare commits

...
This repository has been archived on 2025-01-14. You can view files and clone it, but cannot push or open issues/pull-requests.

4 Commits
master ... v3

Author SHA1 Message Date
liyansheng ddee3af2c1 add<文件上传与下载> 2025-01-14 15:04:36 +08:00
liyansheng 423a8962bb add<请求异步改造> 2025-01-14 00:37:02 +08:00
liyansheng d546287891 add<验证码校验> 2025-01-13 23:54:21 +08:00
liyansheng 66449ed821 add<验证码支持> 2025-01-10 01:49:58 +08:00
10 changed files with 362 additions and 29 deletions

21
pom.xml
View File

@ -48,6 +48,20 @@
<version>0.9.5.5</version> <!-- 最新版本请在 Maven 仓库查看 -->
</dependency>
<!-- Commons FileUpload -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
<!-- Commons IO -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>
<!-- JSTL -->
@ -65,6 +79,13 @@
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
</dependency>
<!-- JSP Implementation (optional, usually provided by your server) -->
<dependency>
<groupId>org.apache.tomcat</groupId>

View File

@ -1,6 +1,8 @@
# jsp+servlet
# 改进版
- 登录验证码
- 图片上传下载
## 全局过滤器的应用(鉴权,异常统一处理)

View File

@ -0,0 +1,64 @@
package example.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 java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Random;
@WebServlet("/captcha")
public class CaptchaServlet extends HttpServlet {
// 处理生成验证码图片的请求
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 设置响应类型为图片
response.setContentType("image/jpeg");
// 设置验证码内容
String captchaText = generateCaptchaText();
// 将验证码存储到 session 中
request.getSession().setAttribute("captcha", captchaText);
// 创建验证码图片
BufferedImage image = new BufferedImage(100, 40, BufferedImage.TYPE_INT_RGB);
Graphics2D graphics = image.createGraphics();
graphics.setColor(Color.WHITE);
graphics.fillRect(0, 0, 100, 40);
graphics.setFont(new Font("Arial", Font.BOLD, 30));
graphics.setColor(Color.BLACK);
graphics.drawString(captchaText, 10, 30);
// 输出验证码图片
OutputStream out = response.getOutputStream();
javax.imageio.ImageIO.write(image, "JPEG", out);
out.close();
}
// 生成随机的验证码文本
private String generateCaptchaText() {
String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
StringBuilder captcha = new StringBuilder();
Random random = new Random();
for (int i = 0; i < 4; i++) {
int index = random.nextInt(characters.length());
captcha.append(characters.charAt(index));
}
return captcha.toString();
}
// 检查用户提交的验证码是否正确(忽略大小写)
public static boolean validateCaptcha(HttpServletRequest request, String userInputCaptcha) {
String sessionCaptcha = (String) request.getSession().getAttribute("captcha");
if (sessionCaptcha == null) {
return false; // 验证码已过期或不存在
}
return sessionCaptcha.equalsIgnoreCase(userInputCaptcha);
}
}

View File

@ -0,0 +1,57 @@
package example.controller;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/download")
public class FileDownloadServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
// 设置文件上传路径webapp/uploads
private static final String UPLOAD_DIRECTORY = "uploads";
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 动态解析 webapp/uploads 的路径
String uploadPath = getServletContext().getRealPath("/") + UPLOAD_DIRECTORY;
// 获取请求的文件名
String fileName = request.getParameter("file");
if (fileName == null || fileName.isEmpty()) {
response.getWriter().println("File parameter is missing");
return;
}
// 构造文件的完整路径
File file = new File(uploadPath + File.separator + fileName);
// 检查文件是否存在
if (!file.exists()) {
response.getWriter().println("File not found: " + fileName);
return;
}
// 设置响应的内容类型
response.setContentType(getServletContext().getMimeType(file.getName()));
response.setContentLength((int) file.length());
response.setHeader("Content-Disposition", "inline; filename=\"" + fileName + "\"");
// 读取文件并写入到响应输出流
try (FileInputStream fis = new FileInputStream(file);
OutputStream os = response.getOutputStream()) {
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = fis.read(buffer)) != -1) {
os.write(buffer, 0, bytesRead);
}
}
}
}

View File

@ -1,5 +1,6 @@
package example.controller;
import example.model.Result;
import example.model.User;
import example.service.IUserService;
import example.service.impl.UserServiceImpl;
@ -18,21 +19,29 @@ public class LoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
resp.setContentType("application/json;charset=utf-8");
req.setCharacterEncoding("utf-8");
String username = req.getParameter("username");
String password = req.getParameter("password");
String captcha = req.getParameter("captcha");
try {
if (!captcha.equalsIgnoreCase((String) req.getSession().getAttribute("captcha"))) {
resp.getWriter().write(toJson(Result.error("验证码错误")));
return;
}
User user = userService.login(username, password);
if(user!=null){
req.getSession().setAttribute("user",user);
resp.sendRedirect("/computer?action=list");
}else{
req.getSession().setAttribute("msg","用户名或密码错误");
resp.sendRedirect("/msg.jsp");
if (user != null) {
req.getSession().setAttribute("user", user);
resp.getWriter().write(toJson(Result.success("登录成功")));
} else {
resp.getWriter().write(toJson(Result.error("用户名或密码错误")));
}
} catch (Exception e) {
throw new RuntimeException(e);
e.printStackTrace();
resp.getWriter().write(toJson(Result.error("系统错误,请稍后重试")));
}
}
@ -40,4 +49,10 @@ public class LoginServlet extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.sendRedirect("/login.jsp");
}
private String toJson(Object object) {
// 使用简单的 JSON 序列化方式,实际可以使用 Jackson 或 Gson 库
return new com.google.gson.Gson().toJson(object);
}
}

View File

@ -0,0 +1,83 @@
package example.controller;
import java.io.File;
import java.io.IOException;
import java.util.List;
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 org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
@WebServlet("/UploadServlet")
public class UploadServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
// 设置上传目录
private static final String UPLOAD_DIRECTORY = "uploads";
// 上传配置
private static final int MEMORY_THRESHOLD = 1024 * 1024 * 3; // 3MB
private static final int MAX_FILE_SIZE = 1024 * 1024 * 40; // 40MB
private static final int MAX_REQUEST_SIZE = 1024 * 1024 * 50; // 50MB
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 检查请求是否为多部分内容
if (!ServletFileUpload.isMultipartContent(request)) {
response.getWriter().println("Error: Form must have enctype=multipart/form-data.");
return;
}
// 配置上传参数
DiskFileItemFactory factory = new DiskFileItemFactory();
factory.setSizeThreshold(MEMORY_THRESHOLD);
factory.setRepository(new File(System.getProperty("java.io.tmpdir")));
ServletFileUpload upload = new ServletFileUpload(factory);
upload.setFileSizeMax(MAX_FILE_SIZE);
upload.setSizeMax(MAX_REQUEST_SIZE);
// 构造上传路径
String uploadPath = getServletContext().getRealPath("") + File.separator + UPLOAD_DIRECTORY;
// 如果目录不存在,则创建
File uploadDir = new File(uploadPath);
if (!uploadDir.exists()) {
uploadDir.mkdir();
}
try {
// 解析请求的内容
@SuppressWarnings("unchecked")
List<FileItem> formItems = upload.parseRequest(request);
if (formItems != null && formItems.size() > 0) {
for (FileItem item : formItems) {
// 处理非表单字段
if (!item.isFormField()) {
String fileName = new File(item.getName()).getName();
String filePath = uploadPath + File.separator + fileName;
File storeFile = new File(filePath);
// 保存文件到磁盘
item.write(storeFile);
request.setAttribute("message", "文件上传成功: " + fileName);
request.setAttribute("fileName",fileName);
}
}
}
} catch (Exception ex) {
request.setAttribute("message", "错误信息: " + ex.getMessage());
}
// 转发到 JSP 显示消息
getServletContext().getRequestDispatcher("/msg.jsp").forward(request, response);
}
}

View File

@ -0,0 +1,41 @@
package example.model;
public class Result<T> {
private String status; // "success" 或 "error"
private String message; // 消息内容
private T data; // 附加数据
public Result(String status, String message, T data) {
this.status = status;
this.message = message;
this.data = data;
}
public Result(String status, String message) {
this(status, message, null);
}
public String getStatus() {
return status;
}
public String getMessage() {
return message;
}
public T getData() {
return data;
}
public static <T> Result<T> success(String message, T data) {
return new Result<>("success", message, data);
}
public static <T> Result<T> success(String message) {
return success(message, null);
}
public static <T> Result<T> error(String message) {
return new Result<>("error", message, null);
}
}

View File

@ -28,6 +28,13 @@
</c:if>
</div>
<form action="UploadServlet" method="post" enctype="multipart/form-data">
<label for="file">选择图片:</label>
<input type="file" name="file" id="file" required>
<button type="submit">上传</button>
</form>
</body>
</html>

View File

@ -1,23 +1,61 @@
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html>
<!DOCTYPE html>
<html>
<head>
<script src="https://www.liyansheng.top/cdn/watermark.js"></script>
</head>
<head>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
$(document).ready(function () {
// 绑定表单提交事件
$('#loginForm').submit(function (event) {
event.preventDefault(); // 阻止表单默认提交行为
<body>
<h2>-电脑商城-用户登录</h2>
<form action="login" method="post">
<label for="username">用户名:</label>
<input type="text" id="username" name="username" required><br><br>
<label for="password">密码:</label>
<input type="password" id="password" name="password" required><br><br>
<button type="submit">登录</button>
<a href="/register">没有账号?去注册</a>
</form>
<br>
${requestScope.msg}
</body>
// 获取表单数据
const formData = $(this).serialize();
</html>
// 异步请求
$.ajax({
url: '/login',
type: 'POST',
data: formData,
dataType: 'json',
success: function (response) {
if (response.status === 'success') {
alert(response.message);
window.location.href = '/computer?action=list'; // 跳转至主页
} else {
$('#message').text(response.message).css('color', 'red');
}
},
error: function () {
$('#message').text('请求失败,请稍后重试').css('color', 'red');
}
});
});
// 刷新验证码
$('#captchaImage').click(function () {
$(this).attr('src', '/captcha?timestamp=' + new Date().getTime());
});
});
</script>
</head>
<body>
<h2>-电脑商城-用户登录</h2>
<form id="loginForm">
<label for="username">用户名:</label>
<input type="text" id="username" name="username" required><br><br>
<label for="password">密码:</label>
<input type="password" id="password" name="password" required><br><br>
<label for="captcha">验证码</label>
<input type="text" id="captcha" name="captcha" required>
<img id="captchaImage" src="/captcha" alt="验证码" style="cursor: pointer;" title="点击刷新验证码"><br><br>
<button type="submit">登录</button>
<a href="/register">没有账号?去注册</a>
</form>
<br>
<p id="message"></p>
</body>
</html>

View File

@ -9,6 +9,11 @@
<body>
<h2>提示:</h2>
<h3>${sessionScope.msg}</h3>
<h3>${message}</h3>
<!-- 动态生成图片标签 -->
<c:if test="${not empty fileName}">
<img src="/download?file=/${fileName}" alt="Uploaded Image" style="max-width: 100%; height: auto;">
</c:if>
<button onclick="window.history.back();">返回</button>
</body>