在实际开发中,登录验证几乎是每个Web应用都绕不开的环节。用户访问受保护资源前,必须确认其身份是否合法——这事儿听起来简单,做起来却有不少细节。今天就来拆解一个经典的JSP+Servlet+Filter实现方案,看看如何用过滤器统一拦截未登录请求。
先看项目的整体结构。新建一个Dynamic Web Project,里面包含1个过滤器(Filter)、1个Servlet和2个JSP页面。具体布局参考下图:

接下来是核心代码部分。先看LoginFilter.ja va,它负责拦截所有请求,判断用户是否已登录:
package filter;
import ja va.io.IOException;
import ja va.io.PrintWriter;
import ja vax.servlet.Filter;
import ja vax.servlet.FilterChain;
import ja vax.servlet.FilterConfig;
import ja vax.servlet.ServletException;
import ja vax.servlet.ServletRequest;
import ja vax.servlet.ServletResponse;
import ja vax.servlet.annotation.WebFilter;
import ja vax.servlet.http.HttpServletRequest;
import ja vax.servlet.http.HttpServletResponse;
import ja vax.servlet.http.HttpSession;
@WebFilter(filterName = "loginFilter", urlPatterns = { "/*" })
public class LoginFilter implements Filter {
public void destroy() { }
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
HttpSession session = req.getSession(true);
resp.setContentType("text/html;");
resp.setCharacterEncoding("utf-8");
PrintWriter out = resp.getWriter();
String request_uri = req.getRequestURI();
String ctxPath = req.getContextPath();
String uri = request_uri.substring(ctxPath.length());
if (uri.contains("login.jsp") || uri.contains("loginServlet")) {
chain.doFilter(request, response);
} else {
if (session.getAttribute("user") != null) {
chain.doFilter(request, response);
} else {
out.println("您没有登录,请先登录!3秒后回到登录页面。");
resp.setHeader("refresh", "3;url=" + ctxPath + "/login.jsp");
return;
}
}
}
public void init(FilterConfig fConfig) throws ServletException { }
}
逻辑很清晰:如果请求路径是登录页面或登录Servlet,直接放行;否则检查session中是否有user属性,有则放行,无则提示“未登录”并3秒后跳转回登录页。注意这里用到了注解@WebFilter,省去了web.xml配置,比较方便。
接着是LoginServlet.ja va,负责处理登录表单提交:
package servlet;
import ja va.io.IOException;
import ja vax.servlet.ServletException;
import ja vax.servlet.annotation.WebServlet;
import ja vax.servlet.http.HttpServlet;
import ja vax.servlet.http.HttpServletRequest;
import ja vax.servlet.http.HttpServletResponse;
import ja vax.servlet.http.HttpSession;
@WebServlet(name = "loginServlet", urlPatterns = { "/loginServlet" })
public class LoginServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("name");
String password = request.getParameter("pwd");
if ("filter".equals(username) && "filter".equals(password)) {
HttpSession session = request.getSession(true);
session.setAttribute("user", username);
response.sendRedirect("loginSuccess.jsp");
} else {
response.sendRedirect("login.jsp");
}
}
}
这里硬编码了用户名和密码均为“filter”,实际项目中当然会从数据库校验,但作为示例已经足够演示流程。登录成功后,将用户名存入session并跳转到success页面。
再看login.jsp,这是用户看到的登录界面:
<%@page import="ja va.util.Date"%> <%@ page language="ja va" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>example
一个简单的表单,背景色设置成了lightPink,提交后数据会发到loginServlet。登录成功后跳转到loginSuccess.jsp:
<%@page import="ja va.util.Date"%> <%@ page language="ja va" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>example <% String username = (String) session.getAttribute("user"); %> 恭喜<%=username%>登录成功!
这个页面只做一件事:从session取出用户名,显示“恭喜xxx登录成功”。
最后,在浏览器输入 http://localhost:8080/Servlet/login.jsp 运行,效果如下:


整个流程跑下来,核心要点就是:用Filter统一拦截,对登录相关资源放行,对其他资源检查session状态。这种方式避免了在每个受保护页面中重复写验证代码,维护起来也方便。当然,生产环境还需要考虑密码加密、会话超时、CSRF防护等,但作为入门示例,这个结构已经足够清晰了。