Я пишу веб-приложение Spring Boot 2. Он использует Azure AD для аутентификации пользователя (с использованием потока кода авторизации и делегированных разрешений), используя поддержку входа в Spring Security OAuth 2.0. Он также пытается получить доступ к ресурсам Microsoft Graph. Однако он не использует библиотеку Microsoft Azure для Spring.
По большей части приведенный ниже код работает как положено. (Я запускаю его локально.)
@Autowired
private RestTemplateBuilder restTemplateBuilder;
@RequestMapping("/aboutMe")
public String aboutMe(Model model,
@RegisteredOAuth2AuthorizedClient("azure") OAuth2AuthorizedClient authorizedClient) {
String accessTokenValue = authorizedClient.getAccessToken().getTokenValue();
RestTemplate restTemplate = buildRestTemplate(accessTokenValue);
String user = restTemplate.getForObject("https://graph.microsoft.com/v1.0/me", String.class);
model.addAttribute("user", user);
return "pages/me";
}
private RestTemplate buildRestTemplate(String accessTokenValue) {
RestTemplate restTemplate = restTemplateBuilder.additionalInterceptors(
new ClientHttpRequestInterceptor() {
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] bytes,
ClientHttpRequestExecution execution) throws IOException {
request.getHeaders().add("Authorization", "Bearer " + accessTokenValue);
return execution.execute(request, bytes);
}
}).build();
return restTemplate;
}
Однако следующий тестовый пример работает, как и ожидалось, до тех пор, пока приложение не будет перезапущено.
Контрольный пример
- Откройте браузер в первый раз и перейдите к
http://localhost:8080/aboutMe
.
- Браузер перейдет на страницу входа Microsoft. Войдите в систему, используя учетные данные Azure AD.
- После этого браузер перейдет на страницу
/aboutMe
.
- Закройте приложение и запустите его снова.
- Не закрывайте браузер. Оставьте это открытым.
- Попробуйте еще раз перейти к
http://localhost:8080/aboutMe
.
Фактический результат
- Браузер перенаправлен на конечную точку Microsoft
https://login.microsoftonline.com/[tenantId]/oauth2/v2.0/authorize
.
- Запрос Microsoft
authorize
затем перенаправляет браузер на redirect-uri
приложения, которое в этом случае начинается с http://localhost:8080/login/oauth2/code/azure
.
- Приложение отвечает 404 - Не найдено.
Ожидаемый результат
Или:
- Azure AD должен снова отобразить страницу входа в систему и попросить пользователя снова пройти аутентификацию, а затем приложение должно правильно обработать ее;
- или Либо приложение, либо Spring, либо оба должны обрабатывать запрос на перенаправление вместо возврата 404.
Исследование
Кажется, что 404 из-за того, что redirect-uri
приложения не всегда "слушает", особенно в этом случае.
В приведенном выше коде контроллера @RegisteredOAuth2AuthorizedClient("azure")
заставляет OAuth2AuthorizedClientArgumentResolver
Spring Security выбросить ClientAuthorizationRequiredException
, потому что он не может найти OAuth2AuthorizedClient
- что мы и ожидали, учитывая, что приложение было перезапущен.
Затем ClientAuthorizationRequiredException
заставляет OAuth2AuthorizationRequestRedirectFilter
перенаправить браузер в Azure для запроса авторизации.
Однако вместо того, чтобы запрашивать учетные данные пользователя, Azure перенаправляет браузер на приложение redirect-uri
, что выглядит как новая авторизация. (Я предполагаю, что это потому, что браузер отправил файлы cookie со своим запросом, который Azure считает действительным.)
Наконец, мой вопрос
Какой лучший способ справиться с такой ситуацией, чтобы не запутать пользователя? Конечно, я всегда могу сказать пользователям просто закрыть все окна браузера и повторить попытку. Однако это поведение само по себе совсем не чисто с точки зрения пользователя.
Я считаю, что конечная точка /login/oauth2/code/azure
обычно обрабатывается фильтрами Spring Security, поэтому я не уверен, как это перехватить.
Может быть, решение состоит в том, чтобы приложение запускало все свои куки в браузере при запуске? Но как насчет печенья Azure?