Я новичок в Spring Security и хочу внедрить клиент для защищенной службы OAUTH2, который принимает только разрешение password
.
Получение access_token
с сервера аутентификации выполняется с использованием данных вhttp body вроде этого:
client_id={{clientId}}&client_secret={{client_secret}}&grant_type=password&username={{username}}&password={{password}}
Впоследствии access_token
необходимо использовать в поле заголовка Authorization
для доступа к фактической услуге.(например, Authorization=Bearer <access_token>
)
Моя цель - использовать предоставляемые функции Spring Security OAuth2 для запроса access_token
от службы аутентификации и использовать его для доступа к конечным точкам службы до истечения срока действия токена.Мне также нравится, что мой access_token
автоматически обновляется с использованием значения refresh_token
с сервера авторизации.Я хочу достичь этого, полностью используя возможности Spring.
Я обнаружил, что могу использовать OAuth2RestTemplate
с ResourceOwnerPasswordResourceDetails
для grant_type password
.
Пост StackOverflow oAuth2Клиент с предоставлением пароля в Spring Security был очень полезен для меня, но я не получил его на работу.Я также обнаружил, что сообщение Проверка подлинности требуется для получения токена доступа (анонимный доступ запрещен) , где пользователь столкнулся с тем же исключением, но использует client_credentials
и AuthorizationCodeResourceDetails
.
.в тот момент, когда мой код выглядит следующим образом.
@Service
public class MyClient {
@Autowired
private OAuth2RestTemplate restTemplate;
@Value("${authServer.accessTokenUri}")
private String accessTokenUri;
@Value("${authServer.clientId}")
private String clientId;
@Value("${authServer.clientSecret}")
private String clientSecret;
@Value("${authServer.username}")
private String username;
@Value("${authServer.password}")
private String password;
@Value("${serviceUrl}")
private String serviceUrl;
@Bean
public OAuth2RestTemplate restTemplate(OAuth2ClientContext oauth2ClientContext) {
OAuth2RestTemplate template = new OAuth2RestTemplate(resource(), oauth2ClientContext);
template.setAccessTokenProvider(accessTokenProvider());
return template;
}
@Bean
public AccessTokenProvider accessTokenProvider() {
ResourceOwnerPasswordAccessTokenProvider tokenProvider = new ResourceOwnerPasswordAccessTokenProvider();
return new AccessTokenProviderChain(
Arrays.<AccessTokenProvider>asList(tokenProvider)
);
}
@Bean
protected OAuth2ProtectedResourceDetails resource() {
ResourceOwnerPasswordResourceDetails resource = new ResourceOwnerPasswordResourceDetails();
resource.setId(clientId);
resource.setAccessTokenUri(accessTokenUri);
resource.setClientId(clientId);
resource.setClientSecret(clientSecret);
resource.setGrantType("password");
resource.setClientAuthenticationScheme(AuthenticationScheme.form); // fetch access_token by sending authentication data in HTTP Body
resource.setAuthenticationScheme(AuthenticationScheme.header); // send access_token via HTTP Header 'Bearer' field when accessing actual service
resource.setUsername(username);
resource.setPassword(password);
return resource;
}
public void getDataFromService() {
String response = restTemplate.getForObject(serviceUrl, String.class);
}
}
Из-за этого блока в AccessTokenProviderChain
выдается исключение.
if (auth instanceof AnonymousAuthenticationToken) {
if (!resource.isClientOnly()) {
throw new InsufficientAuthenticationException("Authentication is required to obtain an access token (anonymous not allowed)");
}
}
Вот трассировка стека исключений.
org.springframework.security.authentication.InsufficientAuthenticationException: Authentication is required to obtain an access token (anonymous not allowed)
at org.springframework.security.oauth2.client.token.AccessTokenProviderChain.obtainAccessToken(AccessTokenProviderChain.java:91) ~[spring-security-oauth2-2.3.4.RELEASE.jar:na]
at org.springframework.security.oauth2.client.OAuth2RestTemplate.acquireAccessToken(OAuth2RestTemplate.java:221) ~[spring-security-oauth2-2.3.4.RELEASE.jar:na]
at org.springframework.security.oauth2.client.OAuth2RestTemplate.getAccessToken(OAuth2RestTemplate.java:173) ~[spring-security-oauth2-2.3.4.RELEASE.jar:na]
at org.springframework.security.oauth2.client.OAuth2RestTemplate.createRequest(OAuth2RestTemplate.java:105) ~[spring-security-oauth2-2.3.4.RELEASE.jar:na]
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:731) ~[spring-web-5.1.7.RELEASE.jar:5.1.7.RELEASE]
at org.springframework.security.oauth2.client.OAuth2RestTemplate.doExecute(OAuth2RestTemplate.java:128) ~[spring-security-oauth2-2.3.4.RELEASE.jar:na]
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:670) ~[spring-web-5.1.7.RELEASE.jar:5.1.7.RELEASE]
at org.springframework.web.client.RestTemplate.getForObject(RestTemplate.java:311) ~[spring-web-5.1.7.RELEASE.jar:5.1.7.RELEASE]
Как видите, я не могу запросить access_token
.Я не понимаю, почему я получаю это исключение, потому что, если я напрямую запрашиваю access_token
с сервера аутентификации с помощью команды curl, я могу аутентифицироваться, используя только предоставленные данные, как указано.
Я получил вручнуюaccess_token
успешно, как это, при добавлении следующего кода перед вызовом restTemplate.getForObject(...)
.
ResourceOwnerPasswordAccessTokenProvider accessTokenProvider = new ResourceOwnerPasswordAccessTokenProvider();
OAuth2AccessToken token = accessTokenProvider.obtainAccessToken(resource(), new DefaultAccessTokenRequest());
restTemplate.getOAuth2ClientContext().setAccessToken(token);
String token = restTemplate.getAccessToken();
Но ручное получение access_token
не то, что я хочу.Есть ли что-то, что мне не хватает?Можно ли автоматически получить access_token
и обновить его, используя Spring Security с грантом password
?Несмотря на то, что я проверяю код несколько часов на Github, StackOverflow и т. Д. ... Мне не удалось заставить мой код работать.
ОБНОВЛЕНИЕ:
Iобнаружил, что мой экземпляр ResourceOwnerPasswordResourceDetails
внутри моего экземпляра OAuth2RestTemplate
не инициализирован, когда я хочу использовать его внутри getDataFromService()
.(т. е. такие поля, как имя пользователя, являются пустыми).После разъяснений и помощи от @JoeGrandja мой вопрос теперь не нацелен на Spring Security, а скорее на Spring.
Что можно сделать, чтобы использовать аннотации @Value
внутри аннотированного метода @Bean
.В настоящее время, когда restTemplate
создается с использованием аннотированного метода @Bean
resource()
, значения из application.yml
, очевидно, еще не доступны.