1. 项目结构
webapp/
├── WEB-INF/
│ └── web.xml
├── index.jsp
├── login.jsp
├── loginServlet.jsp
├── captcha.jsp
└── success.jsp
2. 生成验证码的Servlet (captcha.jsp)
<%@ page contentType="image/jpeg" %>
<%@ page import="java.awt.*,java.awt.image.*,java.util.*,javax.imageio.*" %>
<%!
// 随机生成颜色
Color getRandColor(int fc, int bc) {
Random random = new Random();
if (fc > 255) fc = 255;
if (bc > 255) bc = 255;
int r = fc + random.nextInt(bc - fc);
int g = fc + random.nextInt(bc - fc);
int b = fc + random.nextInt(bc - fc);
return new Color(r, g, b);
}
%>
<%
// 设置页面不缓存
response.setHeader("Pragma", "No-cache");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 0);
// 在内存中创建图像
int width = 80, height = 30;
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
// 获取图形上下文
Graphics g = image.getGraphics();
// 生成随机类
Random random = new Random();
// 设定背景色
g.setColor(getRandColor(200, 250));
g.fillRect(0, 0, width, height);
// 设定字体
g.setFont(new Font("Times New Roman", Font.PLAIN, 24));
// 画边框
g.setColor(new Color(255, 255, 255));
g.drawRect(0, 0, width - 1, height - 1);
// 随机产生155条干扰线,使图像中的验证码不易被其他程序探测到
g.setColor(getRandColor(160, 200));
for (int i = 0; i < 155; i++) {
int x = random.nextInt(width);
int y = random.nextInt(height);
int xl = random.nextInt(12);
int yl = random.nextInt(12);
g.drawLine(x, y, x + xl, y + yl);
}
// 取随机产生的验证码(4位数字)
String sRand = "";
for (int i = 0; i < 4; i++) {
String rand = String.valueOf(random.nextInt(10));
sRand += rand;
// 将验证码显示到图像中
g.setColor(new Color(20 + random.nextInt(110), 20 + random.nextInt(110), 20 + random.nextInt(110)));
g.drawString(rand, 13 * i + 6, 24);
}
// 将验证码存入SESSION
session.setAttribute("captcha", sRand);
// 图像生效
g.dispose();
// 输出图像到页面
ImageIO.write(image, "JPEG", response.getOutputStream());
response.getOutputStream().flush();
response.getOutputStream().close();
out.clear();
out = pageContext.pushBody();
%>
3. 登录页面 (login.jsp)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html>
<head>
<title>验证码登录</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f4f4f4;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
}
.login-container {
background-color: white;
padding: 30px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
width: 350px;
}
h2 {
text-align: center;
color: #333;
margin-bottom: 30px;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 5px;
color: #555;
font-weight: bold;
}
input[type="text"],
input[type="password"] {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
box-sizing: border-box;
font-size: 14px;
}
.captcha-group {
display: flex;
gap: 10px;
align-items: flex-end;
}
.captcha-input {
flex: 1;
}
.captcha-img {
height: 40px;
border: 1px solid #ddd;
border-radius: 4px;
cursor: pointer;
}
.refresh-btn {
background: none;
border: 1px solid #ddd;
border-radius: 4px;
padding: 5px 10px;
cursor: pointer;
font-size: 12px;
}
.refresh-btn:hover {
background-color: #f5f5f5;
}
.submit-btn {
width: 100%;
padding: 12px;
background-color: #007bff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
font-weight: bold;
}
.submit-btn:hover {
background-color: #0056b3;
}
.error-message {
color: red;
text-align: center;
margin-top: 15px;
font-size: 14px;
display: none;
}
.message {
color: #28a745;
text-align: center;
margin-top: 15px;
font-size: 14px;
}
</style>
</head>
<body>
<div class="login-container">
<h2>用户登录</h2>
<form id="loginForm" method="post" action="loginServlet.jsp">
<div class="form-group">
<label for="username">用户名:</label>
<input type="text" id="username" name="username"
placeholder="请输入用户名" required>
</div>
<div class="form-group">
<label for="password">密码:</label>
<input type="password" id="password" name="password"
placeholder="请输入密码" required>
</div>
<div class="form-group">
<label for="captcha">验证码:</label>
<div class="captcha-group">
<div class="captcha-input">
<input type="text" id="captcha" name="captcha"
placeholder="请输入验证码" required>
</div>
<div>
<img id="captchaImage" src="captcha.jsp"
class="captcha-img" alt="验证码"
onclick="refreshCaptcha()">
</div>
<button type="button" class="refresh-btn"
onclick="refreshCaptcha()">
刷新
</button>
</div>
</div>
<button type="submit" class="submit-btn">登录</button>
<div id="errorMessage" class="error-message"></div>
<%-- 显示消息 --%>
<%
String message = (String) session.getAttribute("message");
if (message != null) {
String messageClass = message.contains("成功") ? "message" : "error-message";
%>
<div class="<%= messageClass %>" style="display: block;">
<%= message %>
</div>
<%
session.removeAttribute("message");
}
%>
</form>
</div>
<script>
// 刷新验证码
function refreshCaptcha() {
var captchaImage = document.getElementById('captchaImage');
// 添加时间戳防止缓存
captchaImage.src = 'captcha.jsp?t=' + new Date().getTime();
document.getElementById('captcha').value = '';
}
// 表单提交验证
document.getElementById('loginForm').onsubmit = function(e) {
e.preventDefault();
var username = document.getElementById('username').value;
var password = document.getElementById('password').value;
var captcha = document.getElementById('captcha').value;
var errorMessage = document.getElementById('errorMessage');
// 清空错误消息
errorMessage.textContent = '';
errorMessage.style.display = 'none';
// 简单的前端验证
if (!username.trim()) {
showError('请输入用户名');
return false;
}
if (!password.trim()) {
showError('请输入密码');
return false;
}
if (!captcha.trim()) {
showError('请输入验证码');
return false;
}
if (captcha.length !== 4) {
showError('验证码必须是4位数字');
return false;
}
// 如果验证通过,提交表单
this.submit();
};
function showError(message) {
var errorMessage = document.getElementById('errorMessage');
errorMessage.textContent = message;
errorMessage.style.display = 'block';
}
// 输入验证码后自动去除空格
document.getElementById('captcha').oninput = function() {
this.value = this.value.replace(/\s/g, '');
};
// 页面加载时自动聚焦到用户名输入框
window.onload = function() {
document.getElementById('username').focus();
};
</script>
</body>
</html>
4. 登录处理Servlet (loginServlet.jsp)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
// 设置字符编码
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
// 获取表单数据
String username = request.getParameter("username");
String password = request.getParameter("password");
String captchaInput = request.getParameter("captcha");
// 获取session中的验证码
String captchaSession = (String) session.getAttribute("captcha");
// 验证验证码
boolean isCaptchaValid = false;
if (captchaSession != null && captchaInput != null) {
isCaptchaValid = captchaSession.equalsIgnoreCase(captchaInput);
}
// 清除session中的验证码(一次性使用)
session.removeAttribute("captcha");
if (!isCaptchaValid) {
// 验证码错误
session.setAttribute("message", "验证码错误,请重新输入");
response.sendRedirect("login.jsp");
return;
}
// 这里可以添加数据库验证(示例使用硬编码验证)
boolean isValidUser = false;
if ("admin".equals(username) && "123456".equals(password)) {
isValidUser = true;
}
if (isValidUser) {
// 登录成功
session.setAttribute("username", username);
session.setAttribute("message", "登录成功!");
response.sendRedirect("success.jsp");
} else {
// 登录失败
session.setAttribute("message", "用户名或密码错误");
response.sendRedirect("login.jsp");
}
%>
5. 登录成功页面 (success.jsp)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html>
<head>
<title>登录成功</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f4f4f4;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
}
.success-container {
background-color: white;
padding: 40px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
text-align: center;
}
h2 {
color: #28a745;
margin-bottom: 20px;
}
.welcome-message {
color: #333;
font-size: 18px;
margin-bottom: 30px;
}
.logout-btn {
background-color: #dc3545;
color: white;
border: none;
padding: 10px 20px;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
}
.logout-btn:hover {
background-color: #c82333;
}
.back-btn {
background-color: #6c757d;
color: white;
border: none;
padding: 10px 20px;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
margin-left: 10px;
}
.back-btn:hover {
background-color: #5a6268;
}
</style>
</head>
<body>
<div class="success-container">
<h2>登录成功!</h2>
<div class="welcome-message">
欢迎您,<strong><%= session.getAttribute("username") %></strong>!
</div>
<button class="logout-btn" onclick="logout()">退出登录</button>
<button class="back-btn" onclick="goBack()">返回登录页</button>
</div>
<script>
function logout() {
if (confirm('确定要退出登录吗?')) {
window.location.href = 'index.jsp';
}
}
function goBack() {
window.location.href = 'login.jsp';
}
</script>
</body>
</html>
6. 首页 (index.jsp)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html>
<head>
<title>首页</title>
<style>
body {
font-family: Arial, sans-serif;
text-align: center;
padding-top: 100px;
background-color: #f4f4f4;
}
.container {
max-width: 600px;
margin: 0 auto;
padding: 30px;
background-color: white;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
h1 {
color: #333;
}
.login-btn {
background-color: #007bff;
color: white;
border: none;
padding: 15px 30px;
border-radius: 4px;
cursor: pointer;
font-size: 18px;
margin-top: 30px;
}
.login-btn:hover {
background-color: #0056b3;
}
</style>
</head>
<body>
<div class="container">
<h1>欢迎来到验证码登录演示系统</h1>
<p>这是一个使用JSP + JavaScript实现的验证码登录功能演示</p>
<%
String username = (String) session.getAttribute("username");
if (username != null) {
%>
<p>当前已登录用户: <strong><%= username %></strong></p>
<button class="login-btn" onclick="window.location.href='success.jsp'">
进入系统
</button>
<%
} else {
%>
<button class="login-btn" onclick="window.location.href='login.jsp'">
前往登录
</button>
<%
}
%>
</div>
</body>
</html>
7. web.xml配置(可选)
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<display-name>CaptchaLoginDemo</display-name>
<session-config>
<session-timeout>30</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
功能说明:
验证码生成:
- 使用Java AWT生成4位数字验证码图片
- 添加干扰线增加安全性
- 验证码存储在session中
前端功能:
- 表单验证(JavaScript)
- 验证码刷新功能
- 友好的用户界面和错误提示
后端验证:
- 验证码校验(不区分大小写)
- 用户名密码验证(示例使用硬编码)
- Session管理
安全性考虑:
- 验证码一次性使用
- 表单提交使用POST方法
- 防止缓存验证码图片
测试账号:
这个实现包含了完整的验证码登录流程,可以直接部署到Tomcat服务器上运行。