“`java,// 生成验证码,BufferedImage image = new BufferedImage(80,30,BufferedImage.TYPE_INT_RGB);,Graphics g = image.getGraphics();,// 绘制干扰线及随机字符(示例),g.drawString(“1234”,5,25);,HttpSession session = request.getSession();,session.setAttribute(“code”,”1234″);,ImageIO.write(image,”JPEG”,response.
环境准备与依赖配置
-
技术栈
- Java EE(Servlet/JSP)
- MySQL数据库(或其他关系型数据库)
- JSON处理库(如FastJSON)
- JDK 1.8+
-
Maven依赖配置
<dependencies> <!-Servlet API --> <dependency>javax.servlet:javax.servlet-api:4.0.1</dependency> <!-MySQL驱动 --> <dependency>mysql:mysql-connector-java:8.0.33</dependency> <!-FastJSON --> <dependency>com.alibaba:fastjson:1.2.83</dependency> </dependencies>
前端页面设计(JSP/HTML)
登录页面(login.jsp
)
<form action="LoginServlet" method="post"> <table> <tr><td>用户名:</td><td><input type="text" name="username" required></td></tr> <tr><td>密码:</td><td><input type="password" name="password" required></td></tr> <tr><td>验证码:</td><td> <img src="CaptchaServlet" alt="点击刷新" onclick="this.src='CaptchaServlet?ts='+Date.now()"> <input type="text" name="captcha" required> </td></tr> <tr><td colspan="2"><input type="submit" value="登录"></td></tr> </table> </form>
组件 | 功能说明 |
---|---|
用户名/密码输入框 | 收集用户凭证信息 |
验证码图片 | 通过CaptchaServlet 动态生成 |
刷新机制 | 点击图片时附加时间戳强制重新生成 |
后端Servlet实现
验证码生成Servlet(CaptchaServlet.java
)
@WebServlet("/CaptchaServlet") public class CaptchaServlet extends HttpServlet { private static final String CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; private static final int WIDTH = 160; // 图片宽度 private static final int HEIGHT = 60; // 图片高度 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 设置响应类型为图片 response.setContentType("image/jpeg"); BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB); Graphics2D g = image.getGraphics(); // 生成随机验证码 String captcha = getRandomString(4); g.setFont(new Font("Arial", Font.BOLD, 40)); g.setColor(Color.WHITE); g.fillRect(0, 0, WIDTH, HEIGHT); // 白色背景 g.setColor(getRandColor(50, 150)); g.drawString(captcha, 20, 45); // 绘制验证码 // 绘制干扰线 g.setColor(getRandColor(160, 200)); for (int i = 0; i < 5; i++) { g.drawLine(rand.nextInt(WIDTH), rand.nextInt(HEIGHT), rand.nextInt(WIDTH), rand.nextInt(HEIGHT)); } // 存储验证码到Session request.getSession().setAttribute("CAPTCHA", captcha); ImageIO.write(image, "JPEG", response.getOutputStream()); } private String getRandomString(int length) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < length; i++) { sb.append(CHARS.charAt(rand.nextInt(CHARS.length()))); } return sb.toString(); } private Color getRandColor(int fc, int bc) { // 生成随机颜色 fc = rand.nextInt(fc); bc = rand.nextInt(bc); return new Color(fc, fc, bc); } }
登录处理Servlet(LoginServlet.java
)
@WebServlet("/LoginServlet") public class LoginServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 获取请求参数 String username = request.getParameter("username"); String password = request.getParameter("password"); String captcha = request.getParameter("captcha"); // 验证验证码 String sessionCaptcha = (String) request.getSession().getAttribute("CAPTCHA"); if (!captcha.equalsIgnoreCase(sessionCaptcha)) { returnJson(response, "验证码错误"); return; } // 验证用户名密码格式 if (!username.matches("^[a-zA-Z0-9_]{3,}$") || !password.matches("^.{6,}$")) { returnJson(response, "用户名或密码格式不正确"); return; } // 查询数据库 User user = userService.queryByUsername(username); if (user == null || !user.getPassword().equals(MD5Util.encrypt(password))) { returnJson(response, "用户名或密码错误"); return; } // 生成Token(示例中使用JWT) String token = JWTUtils.generateToken(user.getId()); returnJson(response, "登录成功", Map.of("token", token)); } private void returnJson(HttpServletResponse response, String msg, Object data) throws IOException { response.setContentType("application/json"); PrintWriter out = response.getWriter(); JSONObject result = new JSONObject(); result.put("msg", msg); if (data != null) { result.put("data", data); } out.print(result.toJSONString()); out.flush(); } }
数据库设计与服务层实现
用户表结构(MySQL示例)
CREATE TABLE `users` ( `id` INT PRIMARY KEY AUTO_INCREMENT, `username` VARCHAR(20) NOT NULL UNIQUE, `password` VARCHAR(64) NOT NULL, -存储MD5加密后的密码 `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP );
用户服务层(UserServiceImpl.java
)
public class UserServiceImpl implements UserService { public User queryByUsername(String username) { String sql = "SELECT id, username, password FROM users WHERE username = ?"; try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { ps.setString(1, username); ResultSet rs = ps.executeQuery(); if (rs.next()) { return new User( rs.getInt("id"), rs.getString("username"), rs.getString("password") ); } } catch (SQLException e) { e.printStackTrace(); } return null; } }
关键技术点说明
功能模块 | 实现要点 |
---|---|
验证码生成 | 使用BufferedImage 绘制随机字符和干扰线,存入Session保证一次性有效 |
密码加密存储 | 使用MD5或更安全的哈希算法(如BCrypt)存储密码 |
防暴力破解 | 限制登录失败次数,添加IP黑名单机制 |
会话管理 | 登录成功后创建Session并关联用户ID,30分钟自动失效 |
完整代码结构
src/
├── main/
│ ├── java/
│ │ ├── com.example.servlet/
│ │ │ ├── LoginServlet.java
│ │ │ └── CaptchaServlet.java
│ │ ├── com.example.service/
│ │ │ ├── UserService.java
│ │ │ └── UserServiceImpl.java
│ │ └── com.example.util/
│ │ ├── MD5Util.java
│ │ └── JWTUtils.java
│ └── webapp/
│ ├── WEB-INF/
│ │ └── web.xml
│ └── login.jsp
FAQs
问题1:验证码刷新后为何仍然显示旧码?
解答:
在<img>
标签中添加onclick
事件,动态追加时间戳参数强制浏览器重新请求验证码。
<img src="CaptchaServlet?ts=123456" onclick="this.src='CaptchaServlet?ts='+Date.now()">
问题2:如何防止验证码被暴力破解?
解答:
- 限制单IP单位时间内的请求次数(如每分钟最多5次)。
- 设置验证码有效期(如生成后5分钟内有效)。
- 使用更高复杂度的验证码(如增加汉字、
原创文章,发布者:酷盾叔,转转请注明出处:https://www.kd.cn/ask/69064.html