Как сопоставить токен OAuth 2 с объектом UserDetails на сервере ресурсов?

У меня есть 2 отдельных приложения Spring Boot, одно из которых служит сервером авторизации OAuth 2, а другое — сервером ресурсов. Я использую Spring RemoteTokenServices на своем сервере ресурсов для проверки токенов с сервера авторизации. Теперь я пытаюсь определить код защищенного контроллера в своем приложении сервера ресурсов, но я не уверен, как сопоставить класс UserDetails с принципалом аутентификации, предоставляемым через механизм OAuth 2.

Я настроил свой сервер авторизации с пользовательским TokenEnhancer, который добавляет больше деталей к токену, так что /oauth/check_token?token=<token> возвращает с настраиваемыми полями, которые я хочу сопоставить с контроллерами моего сервера ресурсов.

В более монолитной конфигурации, где сервер авторизации также является сервером ресурсов, я могу определить методы контроллера, использующие аутентифицированного принципала, следующим образом:

//User implements UserDetails
public Map<String, Object> getResource(@AuthenticationPrincipal User user) {
    //code that uses the user object
}

Однако это, кажется, не работает так же прямо в более распределенном подходе. Сопоставление завершается ошибкой, и параметр user оказывается нулевым объектом. Я попытался использовать следующий подход:

public Map<String, Object> getResource(Authentication authentication) {
    //code that uses the authentication object
}

Хотя приведенный выше код успешно сопоставляет детали аутентификации, он не дает мне прямого доступа к пользовательским полям, которые я установил с помощью TokenEnhancer, о котором я упоминал ранее. Кажется, я не могу найти что-либо в документах Spring по этому поводу.


person Psycho Punch    schedule 11.12.2016    source источник
comment
может быть, вы можете получить пользователя из базы данных или что-то еще, используя вашу подробную службу и идентификатор пользователя   -  person Nikhil    schedule 12.09.2018
comment
У меня такая же проблема, как у вас, не могли бы вы выложить полную конфигурацию   -  person reza ramezani matin    schedule 20.01.2019


Ответы (2)


Чтобы решить эту проблему, позвольте мне сначала пройтись по архитектуре. Объект UserDetails, автоматически отображаемый через @AuthenticationPrincipal, поступает из поля principal активного объекта Authentication. Контроллер сервера ресурсов имеет доступ к объекту OAuth2Authencation, который является специализированным экземпляром Authentication для инфраструктуры безопасности Spring OAuth2, просто объявив его как часть параметров метода.

public void controllerMethod(OAuth2Authentication authentication) {
    //controller definition
}

Зная это, проблема теперь переходит к тому, как убедиться, что метод getPrincipal() в объекте Authentication является экземпляром моего пользовательского класса UserDetails. RemoteTokenServices, который я использую в приложении сервера ресурсов, использует экземпляр AccessTokenConverter для интерпретации сведений о маркере, отправленных сервером авторизации. По умолчанию он использует DefaultAccessTokenConverter, который просто устанавливает принцип аутентификации в качестве имени пользователя, то есть String. Этот преобразователь использует UserAuthenticationConverter для преобразования данных, поступающих с сервера авторизации, в экземпляр Authentication. Это то, что мне нужно было настроить:

DefaultAccessTokenConverter tokenConverter = new DefaultAccessTokenConverter();
tokenConverter.setUserTokenConverter(new DefaultUserAuthenticationConverter() {

    @Override
    public Authentication extractAuthentication(Map<String, ?> map) {
        Authentication authentication = super.extractAuthentication(map);
        // User is my custom UserDetails class
        User user = new User();
        user.setSpecialKey(map.get("specialKey").toString());
        return new UsernamePasswordAuthenticationToken(user,
                authentication.getCredentials(), authentication.getAuthorities());
    }

});
tokenServices.setAccessTokenConverter(tokenConverter);

После всех этих настроек механизм @AuthenticationPrincipal теперь работает как положено.

person Psycho Punch    schedule 14.12.2016
comment
Это решение работает, но это скорее обходной путь, чем исправление. Причина, по которой вы получаете String для Principle, заключается в том, что userDetailsService неправильно внедряется в DefaultUserAuthenticationConverter(). Теперь вопрос, куда его нужно вводить, чтобы этот обходной путь не требовался? - person zalis; 05.04.2017
comment
У вас есть ответ на этот вопрос? Кажется, это нигде не задокументировано - person danielbker; 29.03.2018
comment
Вы также можете напрямую внедрить свою пользовательскую реализацию org.springframework.security.core.userdetails.UserDetailsService следующим образом: JwtAccessTokenConverter jwt = new JwtAccessTokenConverter(); DefaultUserAuthenticationConverter duac = new DefaultUserAuthenticationConverter(); duac.setUserDetailsService(myUserDetailsService); DefaultAccessTokenConverter datc = new DefaultAccessTokenConverter(); datc.setUserTokenConverter(duac); jwt.setAccessTokenConverter(datc); - person andreas; 07.05.2018
comment
Вы бы выложили полный исходник конфигурации. - person reza ramezani matin; 20.01.2019

Вы включили AuthenticationPrincipalArgumentResolver, например, после xml?

<mvc:annotation-driven>
        <mvc:argument-resolvers>
                <bean class="org.springframework.security.web.method.annotation.AuthenticationPrincipalArgumentResolver" />
        </mvc:argument-resolvers>
</mvc:annotation-driven>

И вам нужно реализовать UserDetails, чтобы вернуть свой собственный CustomerUser object, тогда вы можете использовать аннотацию для прямого получения принципала.

person chaoluo    schedule 11.12.2016
comment
Мне никогда не приходилось определять это явно. Я думаю, что преобразователь уже является частью автоматической настройки Spring Boot. - person Psycho Punch; 11.12.2016
comment
@PsychoPunch Это так. - person chrylis -cautiouslyoptimistic-; 12.12.2016