Добавить пользовательский UserDetailsService в приложение Spring Security OAuth2

Как добавить пользовательский UserDetailsService ниже в этот образец Spring OAuth2?

Значение по умолчанию user со значением по умолчанию password определяется в _ 4_ приложения authserver.

Однако я хотел бы добавить следующий пользовательский UserDetailsService в demo пакет приложения authserver для тестирования:

package demo;

import java.util.List;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.provisioning.UserDetailsManager;
import org.springframework.stereotype.Service;

@Service
class Users implements UserDetailsManager {

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        String password;
        List<GrantedAuthority> auth = AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER");
        if (username.equals("Samwise")) {
            auth = AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_HOBBIT");
            password = "TheShire";
        }
        else if (username.equals("Frodo")){
            auth = AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_HOBBIT");
            password = "MyRing";
        }
        else{throw new UsernameNotFoundException("Username was not found. ");}
        return new org.springframework.security.core.userdetails.User(username, password, auth);
    }

    @Override
    public void createUser(UserDetails user) {// TODO Auto-generated method stub
    }

    @Override
    public void updateUser(UserDetails user) {// TODO Auto-generated method stub
    }

    @Override
    public void deleteUser(String username) {// TODO Auto-generated method stub
    }

    @Override
    public void changePassword(String oldPassword, String newPassword) {
        // TODO Auto-generated method stub
    }

    @Override
    public boolean userExists(String username) {
        // TODO Auto-generated method stub
        return false;
    }
}

Как видите, этот UserDetailsService еще не autowired, и он намеренно использует небезопасные пароли, потому что он предназначен только для целей тестирования.

Какие конкретные изменения необходимо внести в образец приложения GitHub, чтобы пользователь мог войти как username=Samwise с password=TheShire или как username=Frodo с password=MyRing? Нужно ли вносить изменения в _ 16_ или в другом месте ?


ПРЕДЛОЖЕНИЯ:


В Spring OAuth2 Developer Guide говорится об использовании GlobalAuthenticationManagerConfigurer, чтобы настроить UserDetailsService глобально. Однако поиск в Google этих имен дает менее чем полезные результаты.

Кроме того, другое приложение, которое использует внутреннюю безопасность Spring INSTEAD OF OAuth, использует следующий синтаксис для подключения UserDetailsService, но я не уверен, как настроить его синтаксис на текущий OP:

@Order(Ordered.HIGHEST_PRECEDENCE)
@Configuration
protected static class AuthenticationSecurity extends GlobalAuthenticationConfigurerAdapter {

    @Autowired
    private Users users;

    @Override
    public void init(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(users);
    }
}

Я попытался использовать @Autowire внутри OAuth2AuthorizationConfig для подключения Users к AuthorizationServerEndpointsConfigurer следующим образом:

@Autowired//THIS IS A TEST
private Users users;//THIS IS A TEST

@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
   endpoints.authenticationManager(authenticationManager)
        .accessTokenConverter(jwtAccessTokenConverter())
        .userDetailsService(users)//DetailsService)//THIS LINE IS A TEST
        ;
}

Но журналы Spring Boot показывают, что пользователь Samwise не был найден, что означает, что UserDetailsService не был успешно подключен. Вот соответствующий отрывок из журналов загрузки Spring:

2016-04-20 15:34:39.998 DEBUG 5535 --- [nio-9999-exec-9] o.s.s.a.dao.DaoAuthenticationProvider    :  
        User 'Samwise' not found
2016-04-20 15:34:39.998 DEBUG 5535 --- [nio-9999-exec-9]   
        w.a.UsernamePasswordAuthenticationFilter :  
        Authentication request failed:  
        org.springframework.security.authentication.BadCredentialsException:  
        Bad credentials

Что еще я могу попробовать?


person CodeMed    schedule 19.04.2016    source источник


Ответы (4)


Я столкнулся с аналогичной проблемой при разработке моего сервера oauth с помощью Spring Security. Моя ситуация была немного другой, так как я хотел добавить UserDetailsService для аутентификации токенов обновления, но я думаю, что мое решение также поможет вам.

Как и вы, я сначала попытался указать UserDetailsService с помощью AuthorizationServerEndpointsConfigurer, но это не сработало. Я не уверен, является ли это ошибкой или преднамеренной, но UserDetailsService необходимо установить в AuthenticationManager, чтобы различные классы oauth2 могли его найти. Это сработало для меня:

@Configuration
@EnableWebSecurity 
public class SecurityConfig extends WebSecurityConfigurerAdapter {

  @Autowired
  Users userDetailsService;

  @Autowired
  public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(userDetailsService);
  }

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
    // other stuff to configure your security
  }

}

Я думаю, если вы измените следующее, начиная со строки 73, это может сработать для вас:

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.parentAuthenticationManager(authenticationManager)
        .userDetailsService(userDetailsService);
}

Вам также, конечно, нужно будет добавить @Autowired Users userDetailsService; где-нибудь в WebSecurityConfigurerAdapter

Еще я хотел упомянуть:

  1. Это может быть конкретной версии, я нахожусь на spring -security-oauth2 2.0.12
  2. Я не могу указать какие-либо источники, почему это так, я даже не уверен, является ли мое решение реальным решением или взломом.
  3. GlobalAuthenticationManagerConfigurer, упомянутый в руководстве, почти наверняка является опечаткой, я не могу найти эту строку где-либо в исходном коде для чего-либо в Spring.
person Manan Mehta    schedule 06.04.2017
comment
Манан, кажется, я нашел глобальный AuthenticationManagerBuilder в коде. Этого не произошло бы с этим строковым литералом, но с globalAuthBuilder. Проверьте мое опубликованное решение ниже. - person Michael Ressler; 23.08.2018

Я столкнулся с той же проблемой и изначально имел то же решение, что и Манан Мехта. Совсем недавно некоторая комбинация версий spring security и spring oauth2 привела к любой попытке обновить токены, что привело к ошибке HTTP 500 с указанием UserDetailsService is required в моих журналах.

Соответствующая трассировка стека выглядит так:

java.lang.IllegalStateException: UserDetailsService is required.
at org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter$UserDetailsServiceDelegator.loadUserByUsername(WebSecurityConfigurerAdapter.java:463)
at org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper.loadUserDetails(UserDetailsByNameServiceWrapper.java:68)
at org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider.authenticate(PreAuthenticatedAuthenticationProvider.java:103)
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:174)
at org.springframework.security.oauth2.provider.token.DefaultTokenServices.refreshAccessToken(DefaultTokenServices.java:150)

Внизу вы видите, что DefaultTokenServices пытается обновить токен. Затем он обращается к AuthenticationManager для повторной аутентификации (в случае, если пользователь отозвал разрешение или пользователь был удален и т. Д.), Но именно здесь все разваливается. Вы видите в верхней части трассировки стека, что UserDetailsServiceDelegator - это то, что вызывает вызов loadUserByUsername вместо моего прекрасного UserDetailsService. Хотя внутри моего WebSecurityConfigurerAdapter я установил UserDetailsService, есть еще два WebSecurityConfigurerAdapter. Один для ResourceServerConfiguration и один для AuthorizationServerSecurityConfiguration, и эти конфигурации никогда не получат UserDetailsService, который я установил.

Прослеживая все процессы Spring Security, чтобы собрать воедино то, что происходит, я обнаружил, что есть «локальный» AuthenticationManagerBuilder и «глобальный» AuthenticationManagerBuilder, и нам нужно установить его в глобальной версии, чтобы эта информация передавалась в эти другие контексты строителя.

Итак, решение, которое я придумал, заключалось в том, чтобы получить «глобальную» версию так же, как другие контексты получали глобальную версию. Внутри моего WebSecurityConfigurerAdapter было следующее:

@Autowired
public void setApplicationContext(ApplicationContext context) {
    super.setApplicationContext(context);
    AuthenticationManagerBuilder globalAuthBuilder = context
            .getBean(AuthenticationManagerBuilder.class);
    try {
        globalAuthBuilder.userDetailsService(userDetailsService);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

И это сработало. В других контекстах теперь был мой UserDetailsService. Я оставляю это здесь для всех храбрых солдат, которые в будущем наткнутся на это минное поле.

person Michael Ressler    schedule 23.08.2018

Моим требованием было получить объект базы данных с обратной стороны атрибута электронной почты oauth2. Я нашел этот вопрос, так как предполагал, что мне нужно создать настраиваемую службу сведений о пользователях. На самом деле мне нужно внедрить интерфейс OidcUser и подключиться к этому процессу.

Сначала я думал, что это OAuth2UserService, но я настроил своего поставщика аутентификации AWS Cognito так, чтобы он подключался по открытому идентификатору.

//inside WebSecurityConfigurerAdapter

http
.oauth2Login()
.userInfoEndpoint()
.oidcUserService(new CustomOidcUserServiceImpl());

...

public class CustomOidcUserServiceImpl implements OAuth2UserService<OidcUserRequest, OidcUser> {

    private OidcUserService oidcUserService = new OidcUserService();

    @Override
    public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException {
        OidcUser oidcUser = oidcUserService.loadUser(userRequest);
        return new CustomUserPrincipal(oidcUser);
    }
}

...

public class CustomUserPrincipal implements OidcUser {

    private OidcUser oidcUser;

    //forward all calls onto the included oidcUser
}

Индивидуальный сервис - это то, куда может пойти любая индивидуальная логика. Я планирую реализовать UserDetails интерфейс на моем CustomUserPrincipal, чтобы у меня были разные механизмы аутентификации для работы и тестирования, чтобы облегчить автоматическое тестирование пользовательского интерфейса.

person James Render    schedule 08.10.2019

У кого-то возникла UserDetailsService is required ошибка при выполнении токена обновления, и вы подтверждаете, что у вас уже есть UserDetailsService bean.

попробуйте добавить это:

@Configuration
public class GlobalSecurityConfig extends GlobalAuthenticationConfigurerAdapter {
    private UserDetailsService userDetailsService;

    public GlobalSecurityConfig(UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    @Override
    public void init(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService);
    }
}

Это моя попытка и ошибка, возможно, не сработает для вас.

Между прочим, если вы откажетесь от догадок, какой bean выберет к весне, вы можете расширить AuthorizationServerConfigurerAdapter и WebSecurityConfigurerAdapter и настроить все самостоятельно, но я думаю, что это теряет силу автоконфигурации Spring. Зачем мне настраивать все, если мне просто нужно настроить какую-то конфигурацию?

person user1686407    schedule 17.06.2019