Аутентификация необходима для получения токена доступа - при использовании разрешения «пароль» и Spring's ResourceOwnerPasswordResourceDetails - PullRequest
0 голосов
/ 10 июня 2019

Я новичок в 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, очевидно, еще не доступны.

1 Ответ

0 голосов
/ 11 июня 2019

Я нашел решение с помощью и поддержкой @JoeGrandja.Большое спасибо!:)

Если у кого-то еще есть проблемы, вот мое рабочее решение.Я также рекомендую прочитать комментарии от @JoeGrandja выше.

@Configuration
@ConfigurationProperties(prefix = "authserver")
public class AuthServerConfigProperties {

    private String accessTokenUri;
    private String clientId;
    private String grantType;
    private String clientSecret;
    private String username;
    private String password;

   // Getter & Setter for all properties ...
}


@Configuration
public class CommConfig {

    @Autowired
    AuthServerConfigProperties configProperties;

    @Bean
    public OAuth2RestOperations restTemplate(OAuth2ClientContext oauth2ClientContext) {
        OAuth2RestTemplate oAuth2RestTemplate = new OAuth2RestTemplate(resource(), oauth2ClientContext);
        oAuth2RestTemplate.setAccessTokenProvider(new ResourceOwnerPasswordAccessTokenProvider());
        return oAuth2RestTemplate;
    }

    @Bean
    protected OAuth2ProtectedResourceDetails resource() {
        ResourceOwnerPasswordResourceDetails resource = new ResourceOwnerPasswordResourceDetails();
        resource.setId(configProperties.getClientId()); // not necessary
        resource.setAccessTokenUri(configProperties.getAccessTokenUri());
        resource.setClientId(configProperties.getClientId());
        resource.setClientSecret(configProperties.getClientSecret());
        resource.setGrantType(configProperties.getGrantType());
        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(configProperties.getUsername());
        resource.setPassword(configProperties.getPassword());
        return resource;
    }
}


@RestController
public class MyController {

    @Autowired
    private OAuth2RestOperations restTemplate;

    @Value("${serviceUrl}")
    private String serviceUrl;

    @RequestMapping(value = "/getData", method = RequestMethod.GET)
    @ResponseBody
    public ResponseEntity<String> getData() {
        String response = restTemplate.getForObject(serviceUrl, String.class);
        return new ResponseEntity(response, HttpStatus.OK);
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...