it-swarm.com.ru

Как мне сопоставить токен 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 по этому поводу.

9
Psycho Punch

Чтобы решить эту проблему, позвольте мне сначала немного рассказать об истории архитектуры. Объект 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 теперь работает как положено.

15
Psycho Punch

Вы включили 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, тогда вы можете использовать аннотацию для непосредственного получения принципала.

0
chaoluo