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 и все заработало...а я целый день сидел и не понимал в чем дело...
Ну ладно, остался все равно один вопрос: как закрыть прямой доступ к сервисам, чтоб маршрутизатор был единственной точкой входа?