自定义过滤器被调用两次[重复]

Posted

技术标签:

【中文标题】自定义过滤器被调用两次[重复]【英文标题】:Custom filter gets called twice [duplicate] 【发布时间】:2021-07-12 07:43:22 【问题描述】:

我正在使用 Okta 在 Spring Boot REST(ful) API 资源服务器中处理身份验证和授权。我从他们提供的一个例子开始here。为了处理用户,我实现了一个过滤器来在每个请求之前存储一个包含uid 声明的实体(在我的关系数据库中),因为我的应用程序的每个路由都需要身份验证。这是过滤器的代码:

@Component
@RequiredArgsConstructor
public class AppUserRegistrationFilter extends OncePerRequestFilter 

    private final AppUserService appUserService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException 
        JwtAuthenticationToken jwtAuthenticationToken = (JwtAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
        
        appUserService.checkAppUserRegistration(jwtAuthenticationToken);

        filterChain.doFilter(httpServletRequest, httpServletResponse);
    

这是网页配置类的代码:

@Configuration
@RequiredArgsConstructor
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter 

    private final AppUserService appUserService;

    @Override
    protected void configure(HttpSecurity http) throws Exception 
        http
                .authorizeRequests()
                .anyRequest()
                .authenticated()
                .and()
                .oauth2ResourceServer()
                .jwt();
        http
                .cors();
        http
                .addFilterAfter(new AppUserRegistrationFilter(appUserService), BearerTokenAuthenticationFilter.class);

        Okta.configureResourceServer401ResponseBody(http);
    


AppUserRepository 没有什么特别之处,它只是扩展了JpaRepository<AppUser, Long>AppUser 是包含 uid 和电子邮件字符串以及用于注册和上次访问时间的几个 LocalDateTime 字段的实体。 最后,这里是来自 AppUserService 实现类的方法的代码:

@Override
public void checkAppUserRegistration(JwtAuthenticationToken jwtAuthenticationToken) 
    String uid = (String) jwtAuthenticationToken.getTokenAttributes().get("uid");
    Optional<AppUser> appUserOptional = appUserRepository.findByUid(uid);
    LocalDateTime now = LocalDateTime.now();
    // By default, the name maps to the sub claim according to Spring Security docs
    String email = jwtAuthenticationToken.getName();
    if (appUserOptional.isEmpty()) 
        AppUser newUser = AppUser.builder()
                .uid(uid)
                .email(email)
                .registrationDateTime(now)
                .lastAccessDateTime(now)
                .build();
        appUserRepository.save(newUser);
     else 
        AppUser appUser = appUserOptional.get();
        if (!appUser.getEmail().equals(email)) 
            // User changed email address, update accordingly
            appUser.setEmail(email);
        
        appUser.setLastAccessDateTime(now);
        appUserRepository.save(appUser);
    

如果我启动前端应用程序 this one,我使用从 Okta 开发人员仪表板创建的用户登录并检查“消息”部分,在后端我看到用户已正确创建,但随后过滤器立即再次被调用,如果我检查数据库,我发现注册日期和上次访问日期不同。为什么过滤器会被调用两次?这是正常行为吗?

【问题讨论】:

【参考方案1】:

Spring Boot 将自动向 Servlet 容器注册任何扩展 Filter 的 Spring bean。

参见 Spring Boot 文档:

Registering Servlets, Filters, and Listeners as Spring Beans

作为 Spring bean 的任何 ServletFilter, 或 servlet *Listener 实例都已注册到嵌入式容器。

由于您的AppUserRegistrationFilter 类被@Component 注释,Spring 会自动为该类创建一个单例bean,因此它会自动注册到Servlet 容器中。

当您调用http.addFilterAfter(new AppUserRegistrationFilter(appUserService), BearerTokenAuthenticationFilter.class); 时,您正在手动创建该类的另一个实例,并将过滤器手动注册到 Servlet 容器。

因此您的代码同时注册了该类的两个不同实例。

解决方案:不要调用addFilterAfter()删除@Component注解。

【讨论】:

以上是关于自定义过滤器被调用两次[重复]的主要内容,如果未能解决你的问题,请参考以下文章

Javascript fetch 调用后端两次 [重复]

在Spring Boot项目中过滤两次调用[重复]

即使对于没有安全设置的端点,也总是调用 Spring Security 过滤器[重复]

drupal 自定义视图过滤器

dubbo 自定义过滤器,打印接口调用信息

Zuul Proxy CORS 头包含多个值,头重复两次 - Java Spring Boot CORS 过滤器配置