Java Servlet登录页面认证绕过安全问题的原因与修复方案
问题原因分析
这个问题我之前在维护Java Web项目时也碰到过,核心原因很明确:你的Controller.java Servlet只处理了「登录表单提交」的场景,却没有对「直接访问Servlet」的请求做强制登录校验。
具体来说:
- 目前的逻辑应该是只有当用户通过index.jsp提交登录表单(POST请求)时,才会调用
Authenticator.java做账号密码验证,验证通过后跳转至Welcome.jsp; - 但如果用户直接通过GET请求访问
http://localhost:8080/web-db-manager/Controller,Servlet并没有检查会话中是否存在已登录的用户标识,直接就执行了跳转逻辑,相当于把需要授权的入口完全暴露了。
修复方案
下面是一步步的具体修复方法,从基础到优化都有:
1. 登录成功后将用户状态存入Session
首先,在登录验证通过后,把用户的登录标识(比如用户名)存入HttpSession,这样后续请求就能通过Session判断用户是否已登录。在Controller.java的POST处理逻辑里添加:
// 当Authenticator验证通过后执行 if (auth.validate(username, password)) { HttpSession session = request.getSession(); session.setAttribute("loggedInUser", username); // 存储已登录用户名,也可以存用户对象 request.getRequestDispatcher("Welcome.jsp").forward(request, response); }
2. 给Controller.java添加全局登录校验
修改Controller.java的doGet和doPost方法,在处理任何请求前先检查Session中的登录状态:
@Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 检查登录状态:false表示如果没有会话就不新建 HttpSession session = request.getSession(false); if (session == null || session.getAttribute("loggedInUser") == null) { // 未登录,直接重定向回登录页 response.sendRedirect("index.jsp"); return; } // 已登录,继续跳转至Welcome.jsp request.getRequestDispatcher("Welcome.jsp").forward(request, response); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String username = request.getParameter("username"); String password = request.getParameter("password"); Authenticator auth = new Authenticator(); if (auth.validate(username, password)) { // 登录成功,存入Session HttpSession session = request.getSession(); session.setAttribute("loggedInUser", username); request.getRequestDispatcher("Welcome.jsp").forward(request, response); } else { // 登录失败,返回登录页并提示错误 request.setAttribute("loginError", "账号或密码错误"); request.getRequestDispatcher("index.jsp").forward(request, response); } }
3. 防止直接访问Welcome.jsp
除了Servlet,最好也在Welcome.jsp的开头添加校验,避免用户直接输入Welcome.jsp的URL绕过Servlet:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <% // 检查登录状态 HttpSession session = request.getSession(false); if (session == null || session.getAttribute("loggedInUser") == null) { response.sendRedirect("index.jsp"); return; } %> <!-- 以下是Welcome.jsp原有内容 --> <!DOCTYPE html> <html> <head> <title>Welcome</title> </head> <body> <h1>欢迎你,<%= session.getAttribute("loggedInUser") %>!</h1> </body> </html>
4. 可选:用Filter统一拦截授权请求
如果以后项目中有更多需要登录才能访问的页面/Servlet,建议写一个统一的LoginFilter,避免每个Servlet都重复写校验逻辑:
public class LoginFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException {} @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; HttpSession session = request.getSession(false); // 定义不需要拦截的路径:登录页、登录提交的POST请求 String requestURI = request.getRequestURI(); if (requestURI.contains("index.jsp") || (requestURI.contains("Controller") && request.getMethod().equalsIgnoreCase("POST"))) { chain.doFilter(req, res); return; } // 校验登录状态 if (session == null || session.getAttribute("loggedInUser") == null) { response.sendRedirect("index.jsp"); } else { chain.doFilter(req, res); } } @Override public void destroy() {} }
然后在web.xml中配置这个Filter:
<filter> <filter-name>LoginFilter</filter-name> <filter-class>com.yourpackage.LoginFilter</filter-class> <!-- 替换成你的Filter所在包路径 --> </filter> <filter-mapping> <filter-name>LoginFilter</filter-name> <url-pattern>/Welcome.jsp</url-pattern> <url-pattern>/Controller</url-pattern> <!-- 可以添加更多需要授权的路径,比如/admin/* --> </filter-mapping>
内容的提问来源于stack exchange,提问作者Kavindu Ravishka
相关产品推荐
相关产品推荐

