Весенняя реактивная безопасность - PullRequest
0 голосов
/ 14 апреля 2020

Я пытаюсь обеспечить реактивную безопасность, а вызовы, не прошедшие проверку подлинности, не отправляются администратору аутентификации.

@Configuration
@EnableWebFluxSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig{
    @Autowired
    private WebAuthenticationManager authenticationManager;

    @Autowired
    private ServerSecurityContextRepository securityContextRepository;

    private static final String[] AUTH_WHITELIST = {
            "/login/**",
            "/logout/**",
            "/authorize/**",
            "/favicon.ico",
    };

    @Bean
    public SecurityWebFilterChain securitygWebFilterChain(ServerHttpSecurity http) {
        return http.exceptionHandling().authenticationEntryPoint((swe, e) -> {
            return Mono.fromRunnable(() -> {
                swe.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            });
        }).accessDeniedHandler((swe, e) -> {
            return Mono.fromRunnable(() -> {
                swe.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
            });
        }).and().csrf().disable()
                .formLogin().disable()
                .httpBasic().disable()
                .authenticationManager(authenticationManager)
                .securityContextRepository(NoOpServerSecurityContextRepository.getInstance())
                .authorizeExchange().pathMatchers(HttpMethod.OPTIONS).permitAll()
                .pathMatchers(AUTH_WHITELIST).permitAll()
                .anyExchange().authenticated().and().build();
    }

    @Bean
    public PBKDF2Encoder passwordEncoder() {
        return new PBKDF2Encoder();
    }


}

Диспетчер WebAuthentication,

@Component
public class WebAuthenticationManager implements ReactiveAuthenticationManager {

    @Autowired
    private JWTUtil jwtUtil;

    @Override
    public Mono<Authentication> authenticate(Authentication authentication) {
        String authToken = authentication.getCredentials().toString();

        String username;
        try {
            username = jwtUtil.getUsernameFromToken(authToken);
        } catch (Exception e) {
            username = null;
        }
        if (username != null && jwtUtil.validateToken(authToken)) {
            Claims claims = jwtUtil.getAllClaimsFromToken(authToken);
            List<String> rolesMap = claims.get("role", List.class);
            List<Role> roles = new ArrayList<>();
            for (String rolemap : rolesMap) {
                roles.add(Role.valueOf(rolemap));
            }
            UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(
                username,
                null,
                roles.stream().map(authority -> new SimpleGrantedAuthority(authority.name())).collect(Collectors.toList())
            );
            return Mono.just(auth);
        } else {
            return Mono.empty();
        }
    }
}

Здесь я зарегистрировал мой менеджер WebAuthentication в Securityconfig. Но, тем не менее, неаутентифицированные вызовы не передаются в WebAuthenticationManager.

Ожидается, что go в AuthenticationManager при попадании на защищенные URL-адреса. Например,

http://localhost: 8080 / api / v1 / user .

Не уверен, почему вызовы не поступают в AuthManager .

В нереактивном случае у нас есть OncePerRequestFilter, и там выполняется проверка подлинности. Не уверен, как реализовать то же самое для реактивных.

1 Ответ

1 голос
/ 19 апреля 2020

Вы отключили все механизмы аутентификации, следовательно, ваш менеджер аутентификации ничего не вызывает. Как вы упомянули, вы можете реализовать аутентификацию через фильтры.

Пример реализации фильтра аутентификации:

    @Bean
    public AuthenticationWebFilter webFilter() {
    AuthenticationWebFilter authenticationWebFilter = new AuthenticationWebFilter(authenticationManager);
    authenticationWebFilter.setServerAuthenticationConverter(tokenAuthenticationConverter());
    authenticationWebFilter.setRequiresAuthenticationMatcher(serverWebExchangeMatcher());
    authenticationWebFilter.setSecurityContextRepository(NoOpServerSecurityContextRepository.getInstance());
    return authenticationWebFilter;
}

Затем добавьте этот фильтр в ServerHttpSecurity: http.addFilterBefore(webFilter(),SecurityWebFiltersOrder.HTTP_BASIC)

Затем, наконец, будет вызван ваш менеджер аутентификации.


Вы должны предоставить несколько дополнительных вещей, чтобы заставить его работать.
Сопоставитель, чтобы проверить, добавлен ли заголовок Authorization к запросу:

    @Bean
    public ServerWebExchangeMatcher serverWebExchangeMatcher() {
    return exchange -> {
        Mono<ServerHttpRequest> request = Mono.just(exchange).map(ServerWebExchange::getRequest);
        return request.map(ServerHttpRequest::getHeaders)
                .filter(h -> h.containsKey(HttpHeaders.AUTHORIZATION))
                .flatMap($ -> ServerWebExchangeMatcher.MatchResult.match())
                .switchIfEmpty(ServerWebExchangeMatcher.MatchResult.notMatch());
    };
}

Преобразователь токенов, ответственный за получение токен из запроса и подготовка basi c AbstractAuthenticationToken

    @Bean
    public ServerAuthenticationConverter tokenAuthenticationConverter() {
    return exchange -> Mono.justOrEmpty(exchange)
            .map(e -> getTokenFromRequest(e))
            .filter(token -> !StringUtils.isEmpty(token))
            .map(token -> getAuthentication(token));
}

Я намеренно пропустил реализацию getTokenFromRequest и getAuthentication, потому что существует множество доступных примеров.

...