Spring security gateway

Не понимаю как настроить авторизацию. У меня есть:

  • eureka-server - наш контейнер в который публикуются сервисы
  • gateway - маршрутизатор
  • service-client - какой-то сервис

Моя цель:

  • на маршрутизаторе сделать авторизацию и проверку доступа перед переходом на какой-либо сервис. (задача хранить сессию/токен в куках у пользователя, а на сервере хранить в redis)
  • закрыть прямой доступ к сервисам, чтоб маршрутизатор был единственной точкой входа

После ввода логина и пароля, получаю код 302 и пустую страницу. Пробую через gateway добраться до сервиса получаю код 200 и пустую страницу. Подскажите пожалуйста как правильно делается, может вообще не в том русле действую. Возможно я не понимаю самого процесса, в теории все понятно вроде бы...

@Configuration
@EnableWebFluxSecurity
@RequiredArgsConstructor
public class GatewayConfig {
    private final AuthenticationGatewayFilterFactory authenticationGatewayFilterFactory;
    private final RouteLocatorBuilder routeLocatorBuilder;

    @Bean
    public RouteLocator customRouteLocator() {
        return routeLocatorBuilder.routes()
                .route("service_route", r -> r.path("/service/**")
                        .filters(f -> f.filter(authenticationGatewayFilterFactory.apply(new Object())))
                        .uri("http://localhost:8082"))
                .build();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}
@Component
@RequiredArgsConstructor
public class AuthenticationGatewayFilterFactory extends AbstractGatewayFilterFactory<Object> {

    private final ReactiveRedisTemplate<String, String> redisTemplate;

    @Override
    public GatewayFilter apply(Object config) {
        // Возвращаем новый фильтр аутентификации
        return (exchange, chain) -> {
            // Получаем список cookie из запроса
            List<String> cookieStrings = exchange.getRequest().getHeaders().get("Cookie");
            if (cookieStrings != null) {
                // Если есть cookie, ищем в них сессию пользователя
                String sessionId = null;
                for (String cookieString : cookieStrings) {
                    HttpCookie httpCookie = HttpCookie.parse(cookieString).get(0);
                    if ("SESSION_ID".equals(httpCookie.getName())) {
                        sessionId = httpCookie.getValue();
                        break;
                    }
                }
                if (sessionId != null) {
                    // Если нашли сессию, проверяем ее в Redis
                    return redisTemplate.opsForValue().get("SESSION:" + sessionId)
                            .flatMap(sessionJson -> {
                                return chain.filter(exchange);
                            })
                            // Если сессия не найдена в Redis
                            .switchIfEmpty(Mono.just(exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED)).then());
                }
            }
            // Если не нашли сессию в cookie или не было cookie в запросе
            return Mono.just(exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED)).then();
        };
    }
}
@RestController
public class AuthController {
    private final ReactiveUserDetailsService userDetailsService;
    private final PasswordEncoder passwordEncoder;
    private final ReactiveRedisOperations<String, String> redisOperations;
    private final Duration sessionTimeout;

    public AuthController(ReactiveUserDetailsService userDetailsService,
                          PasswordEncoder passwordEncoder,
                          ReactiveRedisOperations<String, String> redisOperations,
                          @Value("${session.timeout}") Duration sessionTimeout) {
        this.userDetailsService = userDetailsService;
        this.passwordEncoder = passwordEncoder;
        this.redisOperations = redisOperations;
        this.sessionTimeout = sessionTimeout;
    }

    @GetMapping("/")
    public Mono<String> starting(Mono<Principal> principal) {
        return principal
                .map(Principal::getName)
                .map(name -> String.format("Hello, %s", name));
    }

    @PostMapping("/login")
    public Mono<ResponseEntity<?>> authenticateUser(@RequestBody LoginRequest loginRequest,
                                                    ServerWebExchange exchange) {

        return userDetailsService.findByUsername(loginRequest.getLogin())
                .flatMap(userDetails -> {
                    if (passwordEncoder.matches(loginRequest.getPassword(), userDetails.getPassword())) {
                        // Save session in Redis
                        return createSession(userDetails, exchange);
                    } else {
                        return Mono.just(ResponseEntity.status(HttpStatus.UNAUTHORIZED).build());
                    }

                })
                .defaultIfEmpty(ResponseEntity.status(HttpStatus.UNAUTHORIZED).build());
    }

    private Mono<ResponseEntity<?>> createSession(UserDetails userDetails, ServerWebExchange exchange) {
        String sessionId = UUID.randomUUID().toString();

        // Save session in Redis
        return redisOperations.opsForValue().set(sessionId, userDetails.getUsername())
                .flatMap(result -> {
                    if (result) {
                        // Set session id in cookie
                        exchange.getResponse().addCookie(ResponseCookie.from("SESSION_ID",
                                        sessionId)
                                .maxAge(sessionTimeout)
                                .httpOnly(true)
                                .build());
                        return Mono.just(ResponseEntity.ok("Login successful"));
                    } else {
                        return Mono.just(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build());
                    }
                });
    }
}

=======================

В общем перезапустил idea и все заработало...а я целый день сидел и не понимал в чем дело...

Ну ладно, остался все равно один вопрос: как закрыть прямой доступ к сервисам, чтоб маршрутизатор был единственной точкой входа?


Ответы (0 шт):