У меня есть сервер авторизации и сервер ресурсов (отдельное приложение).
Пока я не хочу использовать токены JWT.
Сервер авторизации предоставляет конечную точку / user / me для получения аутентификации вызывающей стороны:
@GetMapping("/user/me")
public Principal me(final Principal principal) {
return principal;
}
Сервер ресурсов вызывает эту конечную точку, предоставляя в заголовке HTTP токен доступа. Таким образом, при наличии токена доступа сервер ресурсов извлекает аутентификацию, которая создала этот токен.
Когда HTTP-запрос, исходящий от сервера ресурсов, попадает в эту конечную точку (он настраивается со свойством security.oauth2.resource.user-info-uri ), он проходит через OAuth2AuthenticationProcessingFilter .
Этот фильтр извлекает токен доступа из заголовка благодаря TokenExtractor , и полученная (предварительная) аутентификация пытается аутентифицироваться с помощью OAuth2AuthenticationManager :
Authentication authResult = authenticationManager.authenticate(authentication);
Поскольку мой сервер ресурсов и мой сервер авторизации являются двумя разными серверами, диспетчер аутентификации представляет собой UserInfoTokenServices .
OAuth2Authentication auth = tokenServices.loadAuthentication(token);
UserInfoTokenServices выполняет вызов REST для конечной точки / user / me сервера авторизации и отображает извлеченное содержимое во вновь созданную OAuth2Authentication , которая возвращается вернуться к authenticate () .
Моя проблема в том, что это отображение, похоже, неверно.
Ответ REST, возвращаемый / user / me , имеет полномочия и данные пользователя, флаг, указывающий, что они аутентифицированы, раздел для аутентификации пользователя ( userAuthentication ), раздел для аутентификация клиента ( oauth2Request ), а также пара других записей.
Сопоставление, выполненное с помощью UserInfoTokenServices.extractAuthentication () , создает новый OAuth2Authentication , имеющий пустой сохраненный запрос (часть, которая представляет собой аутентификацию клиента). Информация клиента, такая как области и идентификаторы ресурсов, теряется. И действительно, обратно в authenticate () следующий тест пропускается:
Collection<String> resourceIds = auth.getOAuth2Request().getResourceIds();
if (resourceId != null && resourceIds != null && !resourceIds.isEmpty() && !resourceIds.contains(resourceId)) {
throw new OAuth2AccessDeniedException("Invalid token does not contain resource id (" + resourceId + ")");
}
потому что resourceIds пусто, потому что, как уже говорилось, auth - это новая OAuth2Authentication , которая потеряла клиентскую часть аутентификации. Это означает, что клиент, зарегистрированный на моем сервере авторизации с определенными ресурсами, может фактически получить доступ к любому ресурсу. Например. клиент
clientDetailsServiceConfigurer
.inMemory()
.withClient("client")
...
.resourceIds("foo")
...
может получить доступ к ресурсу с bar id.
Почему UserInfoTokenServices.extractAuthentication () делает это и как я могу решить мою проблему?
Чтобы быть абсолютно точным, информация не полностью теряется UserInfoTokenServices.extractAuthentication () , поскольку ответ на вызов REST на / user / me сохраняется в части сведений новой OAuth2Authentication :
token.setDetails(map);
но это бесполезно, так как код здесь не ищет идентификаторы ресурсов.
Для информации я использую код типа предоставления.