通過一個 AuthFilter 串聯:編碼統一 → 安全登錄攔截 → 自動登錄恢復 → 黑名單封禁 → 放行業務。
@WebFilter("/*")
public class AuthFilter implements Filter {
private static final Set<String> BLACK_IPS = ConcurrentHashMap.newKeySet();
private String encoding = "UTF-8";
@Override public void init(FilterConfig cfg) {
encoding = cfg.getInitParameter("encoding");
// 黑名單可從 DB/Redis 載入,這裡演示靜態
BLACK_IPS.addAll(Arrays.asList("192.168.1.100", "10.0.0.5"));
}
@Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
// 1. 編碼統一
request.setCharacterEncoding(encoding);
response.setContentType("text/html;charset=" + encoding);
// 2. 黑名單封禁(最優先,節省資源)
String ip = request.getRemoteAddr();
if (BLACK_IPS.contains(ip)) {
response.setContentType("text/html;charset=UTF-8");
response.getWriter().write("您的 IP 已被封禁 <a href='" + request.getContextPath() + "/login.jsp'>登錄頁</a>");
return;
}
// 3. 安全登錄攔截:排除靜態資源、登錄頁、註冊頁
String uri = request.getRequestURI();
if (isExcluded(uri)) { chain.doFilter(request, response); return; }
HttpSession session = request.getSession(false);
String user = (session != null) ? (String) session.getAttribute("user") : null;
// 4. 自動登錄恢復
if (user == null) {
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie c : cookies) if ("autoLogin".equals(c.getName())) {
String[] uv = c.getValue().split("\\|", 2);
if (uv.length == 2) {
String name = URLDecoder.decode(uv[0], "UTF-8");
String pwd = URLDecoder.decode(uv[1], "UTF-8");
if (userService.validate(name, pwd)) { // 驗證 DB
request.getSession(true).setAttribute("user", name);
user = name; break;
}
}
}
}
}
// 5. 最終放行或攔截
if (user != null) {
chain.doFilter(request, response);
} else {
response.sendRedirect(request.getContextPath() + "/login.jsp");
}
}
private boolean isExcluded(String uri) {
return uri.endsWith("/login.jsp") || uri.endsWith("/login.do")
|| uri.contains("/static/") || uri.contains("/css/") || uri.contains("/js/");
}
@Override public void destroy() {}
}
@WebServlet("/login.do")
public class LoginServlet extends HttpServlet {
@Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String name = req.getParameter("name"), pwd = req.getParameter("pwd");
int days = Integer.parseInt(req.getParameter("remember")); // 0/1/7
if (userService.validate(name, pwd)) {
req.getSession().setAttribute("user", name);
if (days > 0) {
String val = URLEncoder.encode(name, "UTF-8") + "|" + URLEncoder.encode(pwd, "UTF-8");
Cookie c = new Cookie("autoLogin", val);
c.setMaxAge(days * 86400); c.setPath(req.getContextPath());
resp.addCookie(c);
}
resp.sendRedirect(req.getContextPath() + "/index.jsp");
} else {
req.getSession().setAttribute("error", "用戶名或密碼錯誤");
resp.sendRedirect(req.getContextPath() + "/login.jsp");
}
}
}
@WebServlet("/cancelAutoLogin")
public class CancelAutoLoginServlet extends HttpServlet {
@Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
Cookie c = new Cookie("autoLogin", "");
c.setMaxAge(0); c.setPath(req.getContextPath());
resp.addCookie(c);
resp.sendRedirect(req.getContextPath() + "/index.jsp");
}
}
<c:if test="${empty sessionScope.user}">
<form action="<c:url value='/login.do'/>" method="post">
用戶名:<input name="name"/><br/>
密碼:<input name="pwd" type="password"/><br/>
不自動登錄 <input type="radio" name="remember" value="0" checked/>
1 天 <input type="radio" name="remember" value="1"/>
7 天 <input type="radio" name="remember" value="7"/><br/>
<input type="submit" value="登錄"/>
</form>
</c:if>
<c:if test="${!empty sessionScope.user}">
歡迎 ${sessionScope.user} <a href="<c:url value='/cancelAutoLogin'/>">取消自動登錄</a>
</c:if>
HttpOnly、Secure、SameSite=Lax