0

我有一个启用了反应式安全性的 springboot webflux 项目由于某种原因,该项目似乎调用了 ReactiveAuthenticationManager 的身份验证方法两次(一次在默认处理程序中,一次请求到达控制器)。

这是我的示例代码 WebSecurityConfig.class

@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
public class WebSecurityConfig {

  static final String[] AUTH_WHITELIST = {
    "/swagger-resources/**",
    "/swagger-ui.html",
    "/v2/api-docs",
    "/v3/api-docs",
    "/webjars/**",
    "/swagger-ui/**",
    "/v1/healthcheck"
  };
  private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
  private final AuthenticationManager authenticationManager;
  private final SecurityContextRepository securityContextRepository;

  public WebSecurityConfig(
      JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint,
      AuthenticationManager authenticationManager,
      SecurityContextRepository securityContextRepository) {
    this.jwtAuthenticationEntryPoint = jwtAuthenticationEntryPoint;
    this.authenticationManager = authenticationManager;
    this.securityContextRepository = securityContextRepository;
  }

  @Bean
  public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
    return http.securityMatcher(
            new NegatedServerWebExchangeMatcher(
                ServerWebExchangeMatchers.pathMatchers(AUTH_WHITELIST)))
        .exceptionHandling()
        .authenticationEntryPoint(jwtAuthenticationEntryPoint)
        .accessDeniedHandler(new HttpStatusServerAccessDeniedHandler(HttpStatus.BAD_REQUEST))
        .and()
        .csrf()
        .disable()
        .formLogin()
        .disable()
        .httpBasic()
        .disable()
        .logout()
        .disable()
        .authenticationManager(authenticationManager)
        .securityContextRepository(securityContextRepository)
        .authorizeExchange()
        .pathMatchers(HttpMethod.OPTIONS)
        .permitAll()
        .anyExchange()
        .authenticated()
        .and()
        .build();
  }

ServerSecurityContextRepository.class

@Slf4j
@Component
public class SecurityContextRepository implements ServerSecurityContextRepository {

  private final AuthenticationService authenticationService;
  private final AuthenticationManager authenticationManager;

  public SecurityContextRepository(
      AuthenticationService authenticationService, AuthenticationManager authenticationManager) {
    this.authenticationService = authenticationService;
    this.authenticationManager = authenticationManager;
  }

  @Override
  public Mono<Void> save(ServerWebExchange swe, SecurityContext sc) {
    throw new UnsupportedOperationException("Not supported yet.");
  }

  @Override
  public Mono<SecurityContext> load(ServerWebExchange swe) {
    ServerHttpRequest request = swe.getRequest();
    log.info("Parsing Authorization token from Request");
    AuthToken authToken =
        authenticationService.parseRequestToken(authenticationService.getHeaders(request));
    Authentication auth = new UsernamePasswordAuthenticationToken(authToken, null);
    return this.authenticationManager
        .authenticate(auth)
        .map(authentication -> (SecurityContext) new SecurityContextImpl(authentication))

ReactiveAuthenticationManager.class

@Slf4j
@Component
public class AuthenticationManager implements ReactiveAuthenticationManager {

  final AuthenticationService authenticationService;

  @Value("${app.auth_enable}")
  private boolean isAuthEnabled;

  public AuthenticationManager(AuthenticationService authenticationService) {
    this.authenticationService = authenticationService;
  }

  @Override
  public Mono<Authentication> authenticate(Authentication authentication) {
    AuthToken token = (AuthToken) authentication.getPrincipal();
    if (Objects.isNull(token)) {
      log.error("Jwt token not provided");
      return Mono.error(new AuthorizeException("Jwt token not provided"));
    }
    if (isAuthEnabled) {
      return authenticationService
          .verifyRequestToken(token)
          .map(
              aBoolean -> {
                if (!aBoolean) {
                  log.warn("Jwt token not valid");
                  return null;
                }
                log.info("Jwt token is valid");
                return new UsernamePasswordAuthenticationToken(token, null, null);
              });
    }
    return Mono.just(new UsernamePasswordAuthenticationToken(token, null, null));
  }

JwtAuthenticationEntryPoint.class

@Slf4j
@Component
public class JwtAuthenticationEntryPoint extends HttpBasicServerAuthenticationEntryPoint
    implements Serializable {

  private final ObjectMapper objectMapper;

  public JwtAuthenticationEntryPoint(ObjectMapper objectMapper) {
    this.objectMapper = objectMapper;
  }

  @Override
  public Mono<Void> commence(ServerWebExchange exchange, AuthenticationException ex) {
    log.info("Commencing AuthenticationEntryPoint...");
    ServerHttpResponse response = exchange.getResponse();
    JwtAuthenticationError error =
        new JwtAuthenticationError(JwtExceptionContext.getExceptionContext());
    JwtExceptionContext.clearExceptionContext();
    byte[] bytes = new byte[0];
    try {
      bytes = objectMapper.writeValueAsString(error).getBytes(StandardCharsets.UTF_8);
    } catch (JsonProcessingException e) {
      log.error("JsonProcessingException on commence function : {}", e.getMessage(), e);
    }
    DataBuffer buffer = response.bufferFactory().wrap(bytes);
    response.setStatusCode(HttpStatus.valueOf(Integer.parseInt(error.getStatusCode())));
    log.warn(
        "Authentication Failed: {} -> {}",
        value("errorMsg", error),
        keyValue(
            AppConstants.STATUS_CODE, HttpStatus.valueOf(Integer.parseInt(error.getStatusCode()))));
    return response.writeWith(Mono.just(buffer));
  }

示例日志

2022-01-18 15:30:25.203 DEBUG 9308 --- [cTaskExecutor-1] o.s.web.client.RestTemplate              : Reading to [java.lang.String] as "application/json"
2022-01-18 15:30:25.209  INFO 9308 --- [cTaskExecutor-1] c.s.p.r.n.h.s.AuthenticationServiceImpl  : Validation Response 200
2022-01-18 15:30:25.209  INFO 9308 --- [oundedElastic-1] c.s.p.r.n.h.s.AuthenticationManager      : **Jwt token is valid**
2022-01-18 15:30:25.211 DEBUG 9308 --- [oundedElastic-1] o.s.s.w.s.a.AuthorizationWebFilter       : Authorization successful
2022-01-18 15:30:25.217 DEBUG 9308 --- [oundedElastic-1] s.w.r.r.m.a.RequestMappingHandlerMapping : [1fc32c7d-1, L:/0:0:0:0:0:0:0:1:8080 - R:/0:0:0:0:0:0:0:1:54225] Mapped to MyController#getCount(Boolean, String, UsernamePasswordAuthenticationToken)
2022-01-18 15:30:25.255  INFO 9308 --- [oundedElastic-1] c.s.p.r.n.h.s.AuthenticationServiceImpl  : Validation Response 200
2022-01-18 15:30:25.256  INFO 9308 --- [oundedElastic-2] c.s.p.r.n.h.s.AuthenticationManager      : **Jwt token is valid**

任何建议或指针表示赞赏。提前致谢

4

0 回答 0