Использование Zuul в качестве шлюза аутентификации
Фон
Я хочу реализовать проект, представленный в этой статье .
Это можно резюмировать на диаграмме ниже:
![Архитектура безопасности]()
- Клиент сначала выполняет аутентификацию с помощью IDP (OpenID Connect/OAuth2)
- IDP возвращает токен доступа (непрозрачный токен без информации пользователя)
- Клиент делает вызов через шлюз API, используя токен доступа в заголовке авторизации
- Шлюз API делает запрос к IDP с помощью токена доступа
- IDP проверяет, что токен доступа действителен и возвращает пользовательскую информацию в формате JSON.
- Шлюз API хранит информацию пользователя в JWT и подписывает ее с помощью закрытого ключа. Затем JWT передается в службу нисходящего потока, которая проверяет JWT с использованием открытого ключа
- Если служба должна вызывать другую службу для выполнения запроса, она передает JWT, по которому выполняется аутентификация и авторизация для запроса.
Что я до сих пор
У меня больше всего сделано, используя:
- Spring облако как глобальная структура
- Spring для запуска отдельных служб
- Netflix Zuul как шлюз API
Я также написал фильтр Zuul PRE, который проверяет токен доступа, связывается с IDP и создает JWT. Затем JWT добавляется в заголовок для запроса, перенаправленного в нисходящую службу.
Проблема
Теперь мой вопрос весьма специфичен для Zuul и его фильтров. Если по какой-либо причине аутентификация не удалась в шлюзе API, как я могу остановить маршрутизацию и ответить напрямую с помощью 401, не продолжая цепочку фильтров и переадресацию вызова?
В настоящий момент, если аутентификация завершается неудачей, фильтр не добавит JWT в заголовок, а 401 будет поступать из нисходящей службы. Я надеялся, что мой шлюз может предотвратить этот ненужный звонок.
Я попытался понять, как использовать com.netflix.zuul.context.RequestContext
для этого, но документация довольно плохая, и я не мог найти способ.
Ответы
Ответ 1
Вы можете попробовать установить setSendZuulResponse(false)
в текущем контексте. Это не должно направлять запрос. Вы также можете вызвать removeRouteHost()
из контекста, который достигнет того же. Вы можете использовать setResponseStatusCode
для установки кода состояния 401.
Ответ 2
Добавьте в свой метод run следующее: он решит эту проблему
ctx.setSendZuulResponse(false);
ctx.setResponseStatusCode(401);
Ответ 3
Я знаю, что очень поздно ответить
Вы можете подойти с предварительным фильтром зуула. Ниже приведены шаги, которые вы должны выполнить.
//1. create filter with type pre
//2. Set the order of filter to greater than 5 because we need to run our filter after preDecoration filter of zuul.
@Component
public class CustomPreZuulFilter extends ZuulFilter {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public Object run() {
final RequestContext requestContext = RequestContext.getCurrentContext();
logger.info("in zuul filter " + requestContext.getRequest().getRequestURI());
byte[] encoded;
try {
encoded = Base64.encode("fooClientIdPassword:secret".getBytes("UTF-8"));
requestContext.addZuulRequestHeader("Authorization", "Basic " + new String(encoded));
final HttpServletRequest req = requestContext.getRequest();
if (requestContext.getRequest().getHeader("Authorization") == null
&& !req.getContextPath().contains("login")) {
requestContext.unset();
requestContext.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
} else {
//next logic
}
}
} catch (final UnsupportedEncodingException e) {
logger.error("Error occured in pre filter", e);
}
return null;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public int filterOrder() {
return 6;
}
@Override
public String filterType() {
return "pre";
}
}
requestContext.unset() сбрасывает RequestContext для текущего запроса активных потоков. и вы можете предоставить код статуса ответа.