在Java中实现登录注册功能需要结合后端技术、数据库操作和前端交互,以下是一个基于Servlet+JSP+MySQL的详细实现方案,符合现代安全标准和最佳实践:
环境准备
-
开发环境
- JDK 17(LTS版本)
- Apache Tomcat 10.1
- MySQL 8.0(或MariaDB)
- Maven项目管理
-
依赖配置(pom.xml)
<dependencies> <!-- Servlet API --> <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> <version>6.0.0</version> <scope>provided</scope> </dependency> <!-- JSTL --> <dependency> <groupId>jakarta.servlet.jsp.jstl</groupId> <artifactId>jakarta.servlet.jsp.jstl-api</artifactId> <version>3.0.0</version> </dependency> <!-- MySQL驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.33</version> </dependency> <!-- 密码加密 --> <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk18on</artifactId> <version>1.77</version> </dependency> </dependencies>
数据库设计
用户表结构(users)
CREATE TABLE users ( id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) UNIQUE NOT NULL, email VARCHAR(100) UNIQUE NOT NULL, password CHAR(60) NOT NULL, -- BCrypt加密后长度固定60 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP );
注册功能实现
- 密码加密(使用BCrypt)
import org.bouncycastle.crypto.generators.BCrypt;
public class PasswordUtil {
public static String hashPassword(String password) {
byte[] salt = new byte[16];
new SecureRandom().nextBytes(salt);
return BCrypt.generate(password.getBytes(), salt, 12); // 12轮加密
}
public static boolean verifyPassword(String password, String hash) {
return BCrypt.checkPassword(password, hash);
}
2. **注册Servlet**
```java
@WebServlet("/register")
public class RegisterServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) {
String username = request.getParameter("username");
String email = request.getParameter("email");
String password = request.getParameter("password");
// 1. 输入验证
if(username == null || username.trim().isEmpty() ||
!email.matches("^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$")) {
response.sendError(400, "Invalid input");
return;
}
// 2. 密码加密
String hashedPassword = PasswordUtil.hashPassword(password);
// 3. 数据库操作
try (Connection conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASS)) {
String sql = "INSERT INTO users (username, email, password) VALUES (?, ?, ?)";
PreparedStatement stmt = conn.prepareStatement(sql);
stmt.setString(1, username);
stmt.setString(2, email);
stmt.setString(3, hashedPassword);
stmt.executeUpdate();
// 注册成功重定向
response.sendRedirect("login.jsp?success=1");
} catch (SQLException e) {
if (e.getErrorCode() == 1062) {
response.sendRedirect("register.jsp?error=duplicate");
} else {
response.sendError(500);
}
}
}
}
登录功能实现
-
会话管理
@WebServlet("/login") public class LoginServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) { String username = request.getParameter("username"); String password = request.getParameter("password"); try (Connection conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASS)) { String sql = "SELECT id, password FROM users WHERE username = ?"; PreparedStatement stmt = conn.prepareStatement(sql); stmt.setString(1, username); ResultSet rs = stmt.executeQuery(); if (rs.next()) { String storedHash = rs.getString("password"); if (PasswordUtil.verifyPassword(password, storedHash)) { // 创建会话 HttpSession session = request.getSession(); session.setAttribute("userId", rs.getInt("id")); session.setAttribute("username", username); // 设置会话超时30分钟 session.setMaxInactiveInterval(1800); // 重定向到主页 response.sendRedirect("dashboard.jsp"); return; } } response.sendRedirect("login.jsp?error=1"); } catch (SQLException e) { response.sendError(500); } } }
安全性强化措施
-
防御SQL注入
- 全程使用
PreparedStatement
- 输入参数严格校验(长度/字符类型)
- 全程使用
-
密码安全
- 使用BCrypt算法(自适应成本因子)
- 禁止明文存储密码
-
会话保护
// 在Filter中设置 response.setHeader("Set-Cookie", "JSESSIONID=" + session.getId() + "; HttpOnly; Secure; SameSite=Strict");
-
XSS防护
- JSP中使用JSTL转义:
<c:out value="${userInput}"/>
- 设置响应头:
response.setHeader("X-XSS-Protection", "1; mode=block");
- JSP中使用JSTL转义:
前后端交互示例
-
注册表单(register.jsp)
<form action="register" method="post"> <input type="text" name="username" placeholder="用户名" required minlength="3"> <input type="email" name="email" placeholder="邮箱" required> <input type="password" name="password" placeholder="密码" required minlength="8"> <button type="submit">注册</button> <c:if test="${param.error == 'duplicate'}"> <p class="error">用户名或邮箱已存在</p> </c:if> </form>
-
RESTful接口设计
@WebServlet("/api/auth/login") public class AuthApi extends HttpServlet { // 返回JSON格式响应 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException { // ...登录逻辑... response.setContentType("application/json"); PrintWriter out = response.getWriter(); out.print("{"status":"success", "user":"" + username + ""}"); } }
测试要点
-
功能测试
- 注册:重复用户、无效邮箱、弱密码
- 登录:错误密码、不存在的用户
- 会话:超时后重新登录
-
安全测试
- SQL注入尝试:
' OR 1=1 --
- XSS攻击测试:
<script>alert(1)</script>
- 暴力破解防护:增加登录失败锁定机制
- SQL注入尝试:
-
性能测试
- 使用JMeter模拟100并发注册/登录
- BCrypt成本因子调整(10-14轮平衡安全与性能)
最佳实践建议:
- 生产环境使用连接池(如HikariCP)
- 敏感操作记录审计日志
- 重要功能添加二次验证(如短信/邮箱验证码)
- 定期进行安全扫描(使用OWASP ZAP工具)
技术引用:
实现符合现代Web开发标准,通过参数化查询、强密码哈希和会话安全控制,能有效防御常见网络攻击,满足基础认证系统的商业应用需求,实际部署时建议结合Spring Security等框架增强功能。
原创文章,发布者:酷盾叔,转转请注明出处:https://www.kd.cn/ask/26750.html