Spring Boot OAuth 2.0 UserDetails пользователь не найден

Я новичок в Spring Boot и пытаюсь настроить OAuth 2.0. Проблема, с которой я столкнулся в данный момент, заключается в том, что я продолжаю получать следующее сообщение, когда пытаюсь запросить токен доступа:

{"error": "invalid_grant", "error_description": "неверные учетные данные" }

Сообщение об ошибке в консоли Spring Boot говорит, что пользователь не может быть найден.

: Попытка аутентификации с использованием org.springframework.security.authentication.dao.DaoAuthenticationProvider: пользователь 'stromero' не найден: возвращение кэшированного экземпляра одноэлементного компонента 'authenticationAuditListener'

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

Это HTTP-запрос:

POST / oauth / token HTTP / 1.1 Хост: localhost:8181 Авторизация: Basic YnJvd3NlcjpzZWNyZXQ= Cache-Control: no-cache Тип содержимого: application/x-www-form-urlencoded username=stromero&password= пароль &client_id= пароль &client_secret=secret&grant_type= =

Это классы, которые я использовал для реализации своего пользовательского пользователя и OAuth 2.0

@Repository
public interface UserRepository extends CrudRepository<CustomUser, String> {

public CustomUser findByUsername(String name);
}

Ниже пользовательский пользователь, которого я создал

@Entity
@Table (name = "custom_user")
 public class CustomUser {

@Id
@Column(name = "id", nullable = false, updatable = false)
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@Column(name = "username", unique=true, nullable = false)
private String username;
@Column(name = "password", nullable = false)
private String password;
@ElementCollection
private List<String> roles = new ArrayList<>();

public List<String> getRoles() {
    return roles;
}

public void setRoles(List<String> roles) {
    this.roles = roles;
}

public long getId() {
    return id;
}

public void setId(long id) {
    this.id = id;
}

public String getUsername() {
    return username;
}

public void setUsername(String username) {
    this.username = username;
}

public String getPassword() {
    return password;
}

public void setPassword(String password) {
    this.password = password;
}
}

Ниже представлен сервис customdetails, который считывает информацию о пользователе из базы данных и возвращает ее в виде объекта UserDetails.

@Service
@Transactional(readOnly = true)
public class CustomUserDetailsService implements UserDetailsService {

@Autowired
private UserRepository userRepository;

@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {

    CustomUser customUser = userRepository.findByUsername(s);

    boolean enabled = true;
    boolean accountNonExpired = true;
    boolean credentialsNonExpired = true;
    boolean accountNonLocked = true;

    return new User(
            customUser .getUsername(),
            customUser .getPassword().toLowerCase(),
            enabled,
            accountNonExpired,
            credentialsNonExpired,
            accountNonLocked,
            getAuthorities(customUser.getRoles()));
}
public Collection<? extends GrantedAuthority> getAuthorities(List<String> roles) {
    List<GrantedAuthority> authList = getGrantedAuthorities(roles);
    return authList;
}


public static List<GrantedAuthority> getGrantedAuthorities(List<String> roles) {
    List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
    for (String role : roles) {
        authorities.add(new SimpleGrantedAuthority(role));
    }
    return authorities;
}
}

Класс ниже представляет собой структуру данных, которая содержит и UserDetailsService и ClientDetailsService

public class ClientAndUserDetailsService implements UserDetailsService,
    ClientDetailsService {

    private final ClientDetailsService clients;

    private final UserDetailsService users;

    private final ClientDetailsUserDetailsService clientDetailsWrapper;

    public ClientAndUserDetailsService(ClientDetailsService clients,
                                       UserDetailsService users) {
        super();
        this.clients = clients;
        this.users = users;
        clientDetailsWrapper = new ClientDetailsUserDetailsService(this.clients);
    }

    @Override
    public ClientDetails loadClientByClientId(String clientId)
            throws ClientRegistrationException {
        return clients.loadClientByClientId(clientId);
    }

    @Override
    public UserDetails loadUserByUsername(String username)
            throws UsernameNotFoundException {

        UserDetails user = null;
        try{
            user = users.loadUserByUsername(username);
        }catch(UsernameNotFoundException e){
            user = clientDetailsWrapper.loadUserByUsername(username);
        }
        return user;
    }
    }

Класс ниже - моя конфигурация для OAuth 2.0 с использованием Spring Boot

 @Configuration
public class OAuth2SecurityConfiguration {

@Configuration
@EnableWebSecurity
protected static class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    protected void registerAuthentication(
            final AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService);
    }
}


@Configuration
@EnableResourceServer
protected static class ResourceServer extends
        ResourceServerConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {

        http.csrf().disable();

        http.authorizeRequests().antMatchers("/oauth/token").anonymous();

        // Require all GET requests to have client "read" scope
        http.authorizeRequests().antMatchers(HttpMethod.GET, "/**")
                .access("#oauth2.hasScope('read')");

        // Require all POST requests to have client "write" scope
        http.authorizeRequests().antMatchers(HttpMethod.POST,"/**")
                .access("#oauth2.hasScope('write')");
    }

}

@Configuration
@EnableAuthorizationServer
@Order(Ordered.LOWEST_PRECEDENCE - 100)
protected static class AuthorizationServer extends
        AuthorizationServerConfigurerAdapter {

    @Autowired
    private AuthenticationManager authenticationManager;

    private ClientAndUserDetailsService combinedService;

    public AuthorizationServer() throws Exception {

        ClientDetailsService clientDetailsService = new InMemoryClientDetailsServiceBuilder()
                .withClient("browser")
                .secret("secret")
                .authorizedGrantTypes("password")
                .authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
                .scopes("read","write")
                .resourceIds("message")
                .accessTokenValiditySeconds(7200)
                .and()
                .build();

        // Create a series of hard-coded users.
        UserDetailsService userDetailsService = new CustomUserDetailsService();
        combinedService = new ClientAndUserDetailsService(clientDetailsService,   userDetailsService);
    }

    @Bean
    public ClientDetailsService clientDetailsService() throws Exception {
        return combinedService;
    }
    @Bean
    public UserDetailsService userDetailsService() {
        return combinedService;
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints)
            throws Exception {
        endpoints.authenticationManager(authenticationManager);
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients)
            throws Exception {
        clients.withClientDetails(clientDetailsService());
    }

}

}

Ниже мой файл pom.xml

    <properties>
    <tomcat.version>8.0.8</tomcat.version>
</properties>

<dependencies>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-logging</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
    </dependency>

    <!-- Postgres JDBC Driver -->

    <dependency>
        <groupId>org.postgresql</groupId>
        <artifactId>postgresql</artifactId>
        <version>9.2-1002-jdbc4</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>

    <!-- Hibernate validator -->

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>


    <dependency>
        <groupId>org.springframework.security.oauth</groupId>
        <artifactId>spring-security-oauth2</artifactId>
        <version>2.0.3.RELEASE</version>
    </dependency>

    <dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava</artifactId>
        <version>17.0</version>
    </dependency>

</dependencies>

3 ответа

Решение

Да, у меня была такая же проблема... хотел использовать JPA UserDetailsService но та же самая проблема - пользователь не мог быть найден... в конечном итоге он был решен благодаря образцам OAuth2 Дэйва Сайера на GitHub.

Проблема, кажется, в экземпляре authenticationManager, автоматически подключенном в @EnableAuthorizationServer AuthorizationServer учебный класс. AuthenticationManager там подключен автоматически и, кажется, инициализируется по умолчанию DAOAuthenticationProviderи по какой-то причине он не использует пользовательский JPA UserDetailsService мы инициализируем аутентификации Manager с помощью WebSecurityConfiguration,

В примерах Dave Syer authenticationManager выставлен как бин в WebSecurityConfiguration:

    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

затем в AuthorizationServer мы автоматически проводим аутентификацию менеджера следующим образом:

    @Autowired
    @Qualifier("authenticationManagerBean")
    private AuthenticationManager authenticationManager;

Как только я это сделал, мне, наконец, удалось пройти аутентификацию моего пользователя в моем пользовательском репозитории JPA.

InitializeUserDetailsBeanManagerConfigurer имеет порядок по умолчанию как

static final int DEFAULT_ORDER = Ordered.LOWEST_PRECEDENCE - 5000;

Так что Initializee DaoAuthenticationProvider перед пользовательским.

@Order(-5001)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { ... }

Я столкнулся с той же проблемой и провел часы, расследуя это дело. В качестве обходного пути, если вы используете Spring Boot версии 1.1.8.RELEASE, понизьте его до 1.0.2.RELEASE. В таком случае все шло хорошо, но я пока не исследовал причины проблем совместимости с версией Spring Boot 1.1.8.RELEASE.

Другие вопросы по тегам