本系列構建一個完整的 JavaWeb 圖書商城,採用分層架構:Domain → DAO → Service → Servlet → JSP。本文聚焦用戶模塊,實現註冊(郵件激活)、登錄、退出功能。
public class User {
private String uid; // UUID 主鍵
private String username; // 用戶名
private String password; // 密碼 (建議加密存儲)
private String email; // 郵箱
private String code; // 激活碼 (UUID)
private boolean state; // 激活狀態 false=未激活 true=已激活
// getter/setter/toString 省略
}
通過反射根據 method 參數分發請求,減少 Servlet 數量。
public class BaseServlet extends HttpServlet {
protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
res.setContentType("text/html;charset=utf-8");
String method = req.getParameter("method");
try {
Method m = this.getClass().getMethod(method, HttpServletRequest.class, HttpServletResponse.class);
String result = (String) m.invoke(this, req, res);
if (result != null && !result.isEmpty()) {
if (result.startsWith("r:")) res.sendRedirect(req.getContextPath() + result.substring(2));
else req.getRequestDispatcher(result.substring(2)).forward(req, res);
}
} catch (Exception e) { throw new ServletException(e); }
}
}
public class UserServlet extends BaseServlet {
private UserService userService = new UserService();
// 退出登錄
public String quit(HttpServletRequest req, HttpServletResponse resp) {
req.getSession().invalidate();
return "r:/index.jsp";
}
// 登錄
public String login(HttpServletRequest req, HttpServletResponse resp) {
User form = CommonUtils.toBean(req.getParameterMap(), User.class);
try {
User user = userService.login(form);
req.getSession().setAttribute("session_user", user);
req.getSession().setAttribute("cart", new Cart()); // 初始化購物車
return "r:/index.jsp";
} catch (UserException e) {
req.setAttribute("msg", e.getMessage());
req.setAttribute("form", form);
return "f:/jsps/user/login.jsp";
}
}
// 激活
public String active(HttpServletRequest req, HttpServletResponse resp) {
String code = req.getParameter("code");
try {
userService.active(code);
req.setAttribute("msg", "激活成功!請登錄");
} catch (UserException e) {
req.setAttribute("msg", e.getMessage());
}
return "f:/jsps/msg.jsp";
}
// 註冊
public String regist(HttpServletRequest req, HttpServletResponse resp) {
User form = CommonUtils.toBean(req.getParameterMap(), User.class);
form.setUid(CommonUtils.uuid());
form.setCode(CommonUtils.uuid() + CommonUtils.uuid());
// 1. 服務端校驗
Map errors = validate(form);
if (!errors.isEmpty()) {
req.setAttribute("errors", errors);
req.setAttribute("form", form);
return "f:/jsps/user/regist.jsp";
}
// 2. 業務處理
try {
userService.regist(form);
} catch (UserException e) {
req.setAttribute("msg", e.getMessage());
req.setAttribute("form", form);
return "f:/jsps/user/regist.jsp";
}
// 3. 發送激活郵件
sendActivationEmail(form);
req.setAttribute("msg", "註冊成功!請查收郵件激活");
return "f:/jsps/msg.jsp";
}
private Map validate(User form) {
Map errors = new HashMap<>();
if (form.getUsername() == null || form.getUsername().trim().isEmpty()) errors.put("username", "用戶名不能為空");
else if (form.getUsername().length() < 3 || form.getUsername().length() > 10) errors.put("username", "用戶名長度 3-10");
if (form.getPassword() == null || form.getPassword().trim().isEmpty()) errors.put("password", "密碼不能為空");
else if (form.getPassword().length() < 3 || form.getPassword().length() > 10) errors.put("password", "密碼長度 3-10");
if (form.getEmail() == null || form.getEmail().trim().isEmpty()) errors.put("email", "Email不能為空");
else if (!form.getEmail().matches("\\w+@\\w+\\.\\w+")) errors.put("email", "Email格式錯誤");
return errors;
}
private void sendActivationEmail(User user) {
Properties props = new Properties();
try {
props.load(this.getClass().getClassLoader().getResourceAsStream("email_template.properties"));
String host = props.getProperty("host");
String uname = props.getProperty("uname");
String pwd = props.getProperty("pwd");
String from = props.getProperty("from");
String to = user.getEmail();
String subject = props.getProperty("subject");
String content = MessageFormat.format(props.getProperty("content"), user.getCode());
Session session = MailUtils.createSession(host, uname, pwd);
Mail mail = new Mail(from, to, subject, content);
MailUtils.send(session, mail);
} catch (Exception e) { e.printStackTrace(); }
}
}
public class UserService {
private UserDao userDao = new UserDao();
public void regist(User form) throws UserException {
if (userDao.findByUsername(form.getUsername()) != null) throw new UserException("用戶名已存在");
if (userDao.findByEmail(form.getEmail()) != null) throw new UserException("Email已被註冊");
userDao.add(form);
}
public void active(String code) throws UserException {
User user = userDao.findByCode(code);
if (user == null) throw new UserException("激活碼無效");
if (user.isState()) throw new UserException("已激活,請勿重複操作");
userDao.updateState(user.getUid(), true);
}
public User login(User form) throws UserException {
User user = userDao.findByUsername(form.getUsername());
if (user == null) throw new UserException("用戶名不存在");
if (!user.getPassword().equals(form.getPassword())) throw new UserException("密碼錯誤"); // 實際應比對加密值
if (!user.isState()) throw new UserException("賬號未激活");
return user;
}
}
public class UserDao {
private QueryRunner qr = new TxQueryRunner(); // 事務 QueryRunner
public User findByUsername(String username) {
try { return qr.query("select * from tb_user where username=?", new BeanHandler<>(User.class), username); }
catch (SQLException e) { throw new RuntimeException(e); }
}
public User findByEmail(String email) {
try { return qr.query("select * from tb_user where email=?", new BeanHandler<>(User.class), email); }
catch (SQLException e) { throw new RuntimeException(e); }
}
public void add(User user) {
try {
qr.update("insert into tb_user values(?,?,?,?,?,?)",
user.getUid(), user.getUsername(), user.getPassword(), user.getEmail(), user.getCode(), user.isState());
} catch (SQLException e) { throw new RuntimeException(e); }
}
public User findByCode(String code) {
try { return qr.query("select * from tb_user where code=?", new BeanHandler<>(User.class), code); }
catch (SQLException e) { throw new RuntimeException(e); }
}
public void updateState(String uid, boolean state) {
try { qr.update("update tb_user set state=? where uid=?", state, uid); }
catch (SQLException e) { throw new RuntimeException(e); }
}
}
<form action="<c:url value='/UserServlet'>" method="post">
<input type="hidden" name="method" value="regist"/>
用戶名:<input type="text" name="username" value="${form.username}"/> <span>${errors.username}</span><br/>
密碼:<input type="password" name="password" value="${form.password}"/> <span>${errors.password}</span><br/>
郵箱:<input type="text" name="email" value="${form.email}"/> <span>${errors.email}</span><br/>
<input type="submit" value="註冊"/>
</form>
<form action="<c:url value='/UserServlet'>" method="post">
<input type="hidden" name="method" value="login"/>
用戶名:<input type="text" name="username" value="${form.username}"/><br/>
密碼:<input type="password" name="password"/><br/>
<input type="submit" value="登錄"/>
</form>
<c:if test="${not empty msg}"><div style="color:red">${msg}</div></c:if>
CREATE TABLE tb_user (
uid VARCHAR(32) PRIMARY KEY,
username VARCHAR(20) UNIQUE NOT NULL,
password VARCHAR(32) NOT NULL,
email VARCHAR(50) UNIQUE NOT NULL,
code VARCHAR(64) NOT NULL,
state BOOLEAN DEFAULT FALSE
);
host=smtp.163.com
uname=your_email@163.com
pwd=your_auth_code
from=your_email@163.com
subject=圖書商城賬號激活
content=親,點擊鏈接激活賬號:https://localhost:8080/bookstore/UserServlet?method=active&code={0}
用戶模塊是系統安全的第一道防線,代碼需經得起推敲,細節決定成敗。