Play 2.6请求属性添加/读取异常:API认证过滤器无法传递用户对象
解决API认证过滤器中请求属性无法添加的问题
嘿,我之前在实现API认证的时候也踩过这个坑!你遇到的问题大概率是请求对象的不可变性或者过滤器执行时机/顺序导致的,下面给你拆解几个常见原因和对应的解决办法:
1. 检查过滤器中属性设置的时机
过滤器的核心逻辑是要先处理请求、设置属性,再把请求传递给后续的过滤器和控制器。如果你的代码是先调用filterChain.doFilter(request, response),再设置请求属性,那控制器根本拿不到——因为链已经执行完了!
正确的顺序应该是:
@Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletRequest; // 1. 先执行你的Token解析、用户加载逻辑 User currentUser = loadUserFromToken(request); // 你的用户加载方法 // 2. 设置请求属性(这里先别急,往下看第2点) request.setAttribute("currentUser", currentUser); // 3. 最后传递请求给过滤器链 filterChain.doFilter(request, servletResponse); }
2. 用HttpServletRequestWrapper包装请求(关键!)
很多Web框架(比如Spring Boot)会对原始HttpServletRequest做包装,原始对象的setAttribute方法可能不会生效,或者修改无法传递到后续环节。这时候你需要自己包装请求,确保属性能被后续获取:
第一步:创建自定义请求包装类
public class AuthenticatedRequest extends HttpServletRequestWrapper { private final User currentUser; public AuthenticatedRequest(HttpServletRequest request, User currentUser) { super(request); this.currentUser = currentUser; } // 重写getAttribute方法,直接返回我们的用户对象 @Override public Object getAttribute(String name) { if ("currentUser".equals(name)) { return currentUser; } return super.getAttribute(name); } // 兼容后续其他属性的修改 @Override public void setAttribute(String name, Object value) { if ("currentUser".equals(name)) { // 可选:加日志提示用户无法修改认证用户 } else { super.setAttribute(name, value); } } }
第二步:在过滤器中使用包装后的请求
@Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest originalRequest = (HttpServletRequest) servletRequest; User currentUser = loadUserFromToken(originalRequest); // 用自定义包装类包裹原始请求 AuthenticatedRequest wrappedRequest = new AuthenticatedRequest(originalRequest, currentUser); // 传递包装后的请求给后续链 filterChain.doFilter(wrappedRequest, servletResponse); }
3. 确认控制器中获取属性的方式
确保你在控制器里用正确的方式获取属性:
- 直接通过
HttpServletRequest获取:@GetMapping("/user/info") public ResponseEntity<UserInfo> getUserInfo(HttpServletRequest request) { User currentUser = (User) request.getAttribute("currentUser"); // 业务逻辑... } - 或者用Spring的
@RequestAttribute注解(更简洁):@GetMapping("/user/info") public ResponseEntity<UserInfo> getUserInfo(@RequestAttribute("currentUser") User currentUser) { // 业务逻辑... }
4. 检查过滤器的执行顺序
如果你的项目中有多个过滤器,要确保Token认证过滤器的执行顺序在其他可能修改请求的过滤器之前。比如在Spring Boot中,可以用@Order注解指定顺序(数值越小越先执行):
@Component @Order(1) // 确保比其他过滤器先执行 public class TokenAuthenticationFilter implements Filter { // ... 过滤器逻辑 }
最后小排查技巧
如果以上都试过还是不行,可以在过滤器里加日志,打印request.getAttribute("currentUser")是否设置成功,同时在控制器里打印请求对象的类型,看看是不是你包装的AuthenticatedRequest——这样能快速定位是设置环节还是获取环节出了问题。
内容的提问来源于stack exchange,提问作者Jan Wytze
相关产品推荐
相关产品推荐

