java - 将 Spring Security AbstractAuthenticationProcessingFilter 迁移到 WebFlux

标签 java spring spring-security spring-webflux

我正在更新一个旧应用程序以使用 WebFlux,但在使用 Spring Security 处理 JWT 验证时我有点迷失了。

现有代码(适用于标准 Spring Web)如下所示:

(验证 Firebase token )

public class FirebaseAuthenticationTokenFilter extends AbstractAuthenticationProcessingFilter {

  private static final String TOKEN_HEADER = "X-Firebase-Auth";

  public FirebaseAuthenticationTokenFilter() {
    super("/v1/**");
  }

  @Override
  public Authentication attemptAuthentication(
      final HttpServletRequest request, final HttpServletResponse response) {

    for (final Enumeration<?> e = request.getHeaderNames(); e.hasMoreElements(); ) {
      final String nextHeaderName = (String) e.nextElement();
      final String headerValue = request.getHeader(nextHeaderName);
    }

    final String authToken = request.getHeader(TOKEN_HEADER);
    if (Strings.isNullOrEmpty(authToken)) {
      throw new RuntimeException("Invaild auth token");
    }

    return getAuthenticationManager().authenticate(new FirebaseAuthenticationToken(authToken));
  }

但是,当切换到 WebFlux 时,我们会丢失 HttpServletRequestHttpServletResponse。有一个 GitHub 问题表明有替代方法/修复 https://github.com/spring-projects/spring-security/issues/5328然而,随着它的完成,我无法确定实际更改了什么以使这项工作正常进行。

Spring Security 文档虽然很棒,但并没有真正解释如何处理用例。

关于如何进行的任何提示?

最佳答案

最终到达:

首先需要像以前一样使用自定义过滤器更新过滤器链

@Configuration
public class SecurityConfig {

  private final FirebaseAuth firebaseAuth;

  public SecurityConfig(final FirebaseAuth firebaseAuth) {

    this.firebaseAuth = firebaseAuth;
  }

  @Bean
  public SecurityWebFilterChain springSecurityFilterChain(final ServerHttpSecurity http) {

    http.authorizeExchange()
        .and()
        .authorizeExchange()
        .pathMatchers("/v1/**")
        .authenticated()
        .and()
        .addFilterAt(firebaseAuthenticationFilter(), SecurityWebFiltersOrder.AUTHENTICATION)
        .csrf()
        .disable();

    return http.build();
  }

  private AuthenticationWebFilter firebaseAuthenticationFilter() {
    final AuthenticationWebFilter webFilter =
        new AuthenticationWebFilter(new BearerTokenReactiveAuthenticationManager());

    webFilter.setServerAuthenticationConverter(new FirebaseAuthenticationConverter(firebaseAuth));
    webFilter.setRequiresAuthenticationMatcher(ServerWebExchangeMatchers.pathMatchers("/v1/**"));

    return webFilter;
  }
}

该流程的主要工作是 FirebaseAuthenticationConverter,我在其中针对 Firebase 验证传入的 JWT,并针对它执行一些标准逻辑。

@Slf4j
@Component
@RequiredArgsConstructor
public class FirebaseAuthenticationConverter implements ServerAuthenticationConverter {

  private static final String BEARER = "Bearer ";

  private static final Predicate<String> matchBearerLength =
      authValue -> authValue.length() > BEARER.length();

  private static final Function<String, Mono<String>> isolateBearerValue =
      authValue -> Mono.justOrEmpty(authValue.substring(BEARER.length()));

  private final FirebaseAuth firebaseAuth;

  private Mono<FirebaseToken> verifyToken(final String unverifiedToken) {
    try {
      final ApiFuture<FirebaseToken> task = firebaseAuth.verifyIdTokenAsync(unverifiedToken);

      return Mono.justOrEmpty(task.get());
    } catch (final Exception e) {
      throw new SessionAuthenticationException(e.getMessage());
    }
  }

  private Mono<FirebaseUserDetails> buildUserDetails(final FirebaseToken firebaseToken) {
    return Mono.just(
        FirebaseUserDetails.builder()
            .email(firebaseToken.getEmail())
            .picture(firebaseToken.getPicture())
            .userId(firebaseToken.getUid())
            .username(firebaseToken.getName())
            .build());
  }

  private Mono<Authentication> create(final FirebaseUserDetails userDetails) {
    return Mono.justOrEmpty(
        new UsernamePasswordAuthenticationToken(
            userDetails.getEmail(), null, userDetails.getAuthorities()));
  }

  @Override
  public Mono<Authentication> convert(final ServerWebExchange exchange) {
    return Mono.justOrEmpty(exchange)
        .flatMap(AuthorizationHeaderPayload::extract)
        .filter(matchBearerLength)
        .flatMap(isolateBearerValue)
        .flatMap(this::verifyToken)
        .flatMap(this::buildUserDetails)
        .flatMap(this::create);
  }
}

关于java - 将 Spring Security AbstractAuthenticationProcessingFilter 迁移到 WebFlux,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59311395/

相关文章:

java - 具有多个升序值列表的数字排序

mysql - 如何使用 Spring MVC 和 JPA 在 mysql 数据库中持久化图像

Heroku 上的 Grails - spring 安全核心 secureChannel.definition 导致重定向循环

java - Spring - 无法解析 beans.xml 中的测试类

Spring和JUnit,@Transaction注解类和方法的区别?

java - 使用自定义身份验证了解 Spring Security 中的 "Access Denied"

java - Spring Security 不会将 HTTP 重定向到 HTTPS

java - 定义映射时出现 Hibernate 异常

java - (a * b)/c MulDiv 和处理中间乘法溢出

java - JavaScript 中 Java 的 Thread.sleep() 等价物是什么?