Spring 引导和 Spring 安全中的入职过滤器

Onboarding filter in Spring Boot and Spring Security

我正在尝试创建一个过滤器,以便将登录的用户重定向到入职页面,以防他们之前没有完成该过程。

到目前为止,这是我的过滤器:

@Component
@Order(110)
public class OnboardingFilter implements Filter {

  @Override
  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
      throws IOException, ServletException {

    // cast the request and response to HTTP
    HttpServletRequest httpRequest = (HttpServletRequest) request;
    HttpServletResponse httpResponse = (HttpServletResponse) response;
    HttpSession session = httpRequest.getSession(false);

    SecurityContextImpl securityContext = (SecurityContextImpl) session.getAttribute("SPRING_SECURITY_CONTEXT");

    // if there's a logged user
    if (securityContext != null) {
      UserPrincipal principal = (UserPrincipal) securityContext.getAuthentication().getPrincipal();
        
      if (!principal.hasOnboarded()) {
        httpResponse.sendRedirect("/onboarding");
      }
    }

    // continue with the filter chain
    chain.doFilter(httpRequest, httpResponse);
  }

我已经为 @Order 尝试了不同的值,但在每种情况下,http 响应都会生成可下载的内容,而不是显示实际请求的 URL 或发送重定向。有什么想法吗?

我遇到了这个异常:

Caused by: java.lang.IllegalStateException: Committed
    at org.eclipse.jetty.server.HttpChannel.resetBuffer(HttpChannel.java:917)
HttpChannel.java:917
    at org.eclipse.jetty.server.HttpOutput.resetBuffer(HttpOutput.java:1423)
HttpOutput.java:1423
    at org.eclipse.jetty.server.Response.resetBuffer(Response.java:1182)
Response.java:1182
    at org.eclipse.jetty.server.Response.sendRedirect(Response.java:534)
Response.java:534
    at org.eclipse.jetty.server.Response.sendRedirect(Response.java:543)
Response.java:543
    at javax.servlet.http.HttpServletResponseWrapper.sendRedirect(HttpServletResponseWrapper.java:130)
HttpServletResponseWrapper.java:130
    at org.springframework.security.web.firewall.FirewalledResponse.sendRedirect(FirewalledResponse.java:43)
FirewalledResponse.java:43
    at javax.servlet.http.HttpServletResponseWrapper.sendRedirect(HttpServletResponseWrapper.java:130)
HttpServletResponseWrapper.java:130
    at org.springframework.security.web.util.OnCommittedResponseWrapper.sendRedirect(OnCommittedResponseWrapper.java:135)
OnCommittedResponseWrapper.java:135
    at javax.servlet.http.HttpServletResponseWrapper.sendRedirect(HttpServletResponseWrapper.java:130)
HttpServletResponseWrapper.java:130
    at org.springframework.security.web.util.OnCommittedResponseWrapper.sendRedirect(OnCommittedResponseWrapper.java:135)
OnCommittedResponseWrapper.java:135
    at javax.servlet.http.HttpServletResponseWrapper.sendRedirect(HttpServletResponseWrapper.java:130)
HttpServletResponseWrapper.java:130
    at org.springframework.web.servlet.view.RedirectView.sendRedirect(RedirectView.java:627)
RedirectView.java:627
    at org.springframework.web.servlet.view.RedirectView.renderMergedOutputModel(RedirectView.java:314)
RedirectView.java:314
    at org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:316)
AbstractView.java:316
    at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1373)
DispatcherServlet.java:1373
    at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1118)
DispatcherServlet.java:1118
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1057)
DispatcherServlet.java:1057
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943)
DispatcherServlet.java:943
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
FrameworkServlet.java:1006
    ... 94 common frames omitted

中添加一个return怎么样?
httpResponse.sendRedirect("/onboarding");
return;

否则您将继续过滤器链:

chain.doFilter(httpRequest, httpResponse);

不能 100% 确定这是否已经解决了您的问题,但可能也是一个问题。

我最终通过实现 Spring 上下文感知 GenericFilterBean 解决了这个问题,如下所示:

public class OnboardingFilter extends GenericFilterBean {

  @Override
  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
      throws IOException, ServletException {

    HttpServletRequest httpRequest = (HttpServletRequest) request;
    HttpServletResponse httpResponse = (HttpServletResponse) response;

    if (SecurityContextHolder.getContext().getAuthentication() != null) {
      // some business logic
      httpResponse.sendRedirect("/onboarding");
    }
    else {
      chain.doFilter(httpRequest, httpResponse);
    }
  }
}

别忘了将其添加到安全过滤器链中:

@Override
protected void configure(HttpSecurity http) throws Exception {
  http.addFilterAfter(new OnboardingFilter(), UsernamePasswordAuthenticationFilter.class)...