Tomcat 9访问日志无法显示HttpSession登录的%u用户名如何解决
解决方案
Tomcat默认AccessLogValve里的%u占位符读取的是HttpServletRequest.getRemoteUser()返回值,这个值只有在使用Tomcat内置的容器认证(比如Basic认证、JDBC Realm等)时才会被自动填充,你自行存在HttpSession里的username默认不会被日志组件识别,所以对应位置显示为-,这个需求可以通过以下两种方案实现:
方案1:通过Request属性传递用户名(最便捷)
无需改动原有日志结构,只要补充少量代码即可:
- 第一步修改你的
LoginFilter,在doFilter方法逻辑里添加属性赋值:
@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); String loginURI = request.getContextPath() + "/login.jsp"; boolean loggedIn = session != null && session.getAttribute("username") != null; boolean loginRequest = request.getRequestURI().equals(loginURI); // 新增:把用户名存入Request属性,未登录则赋值为- if (loggedIn) { request.setAttribute("access_log_username", session.getAttribute("username")); } else { request.setAttribute("access_log_username", "-"); } // 原有逻辑保持不变 if(loginRequest) { if (loggedIn) { response.sendRedirect(request.getContextPath() + "/system.jsp"); } else { chain.doFilter(request, response); } } else { // 原有其他逻辑 chain.doFilter(request, response); } }
- 第二步修改
server.xml里的AccessLogValve配置,把原来的%u替换为%{access_log_username}r:
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="localhost_access_log" suffix=".txt" pattern="%{X-Forwarded-For}i %l %{access_log_username}r %t "%r" %s %b" />
- 重启Tomcat后即可生效,已登录用户的用户名会正常显示在日志对应位置。
方案2:重写getRemoteUser方法兼容原生%u占位符
如果你不想修改原有日志pattern的结构,可以通过HttpServletRequestWrapper包装请求,重写getRemoteUser方法返回session中的用户名:
- 先在Filter同包下创建请求包装类:
public class UserInfoRequestWrapper extends HttpServletRequestWrapper { public UserInfoRequestWrapper(HttpServletRequest request) { super(request); } @Override public String getRemoteUser() { HttpSession session = getSession(false); if (session != null && session.getAttribute("username") != null) { return (String) session.getAttribute("username"); } return super.getRemoteUser(); } }
- 修改LoginFilter的
doFilter方法,把包装后的请求传递给后续流程:
// 原有逻辑判断完loggedIn之后,传递包装后的请求 chain.doFilter(new UserInfoRequestWrapper(request), response);
- 无需修改server.xml的原有pattern配置,重启Tomcat即可生效。
注意事项
- 如果需要对全站所有请求都记录用户名,需要把
LoginFilter的urlPatterns改为/*,否则仅匹配你当前配置的几个路径的请求会显示用户名。 - 日志是在请求处理完成后输出,只要在请求处理流程中设置的Request属性或包装类逻辑都会被正确读取。
内容的提问来源于stack exchange,提问作者Jan
相关产品推荐
相关产品推荐

