未找到Spring Boot OAuth 2.0 UserDetails用户

Ste*_*ero 6 oauth-2.0 spring-boot spring-security-oauth2 userdetailsservice

我是Spring Boot的新手,我正在尝试配置OAuth 2.0.我现在遇到的问题是,当我尝试请求访问令牌时,我不断收到以下消息:

{"error":"invalid_grant","error_description":"凭据错误"}

Spring Boot控制台中的错误消息表明找不到用户.

:使用org.springframework.security.authentication.dao.DaoAuthenticationProvider验证尝试:用户"stromero"未找到:返回singleton的bean的缓存实例"authenticationAuditListener"

我已经使用JPA实现了已经保存到数据库的自定义用户,我无法确定为什么Spring Security无法找到此用户,这可能是我的逻辑或配置问题.如果具有更多经验的人可以查看我的代码并且可能指导我找到正确的方向,那将非常感激.

这是HTTP请求:

POST /的OAuth /令牌HTTP/1.1主机:本地主机:8181授权:基本YnJvd3NlcjpzZWNyZXQ =缓存控制:无缓存内容类型:应用程序/ x-WWW窗体-urlencoded用户名= stromero和密码=密码&CLIENT_ID =浏览器&client_secret =秘密&grant_type =密码

这些是我用来实现自定义用户和OAuth 2.0的类

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

public CustomUser findByUsername(String name);
}
Run Code Online (Sandbox Code Playgroud)

以下是我创建的自定义用户

@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;
}
}
Run Code Online (Sandbox Code Playgroud)

下面是一个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;
}
}
Run Code Online (Sandbox Code Playgroud)

下面的类是一个包含UserDetailsS​​ervice和ClientDetailsS​​ervice的数据结构

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;
    }
    }
Run Code Online (Sandbox Code Playgroud)

下面的类是我使用Spring Boot配置的OAuth 2.0

 @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());
    }

}

}
Run Code Online (Sandbox Code Playgroud)

下面是我的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>
Run Code Online (Sandbox Code Playgroud)

小智 17

是的,我有同样的问题...想要使用JPA UserDetailsService但是同样的问题 - 用户无法找到...最终得到了解决,感谢Dave Syer在GitHub上的OAuth2样本.

问题似乎是在@EnableAuthorizationServer AuthorizationServer类中自动装配的authenticationManager实例中.AuthenticationManager在那里自动装配,似乎默认初始化DAOAuthenticationProvider,并且由于某种原因它不使用自定义JPA UserDetailsService我们用in初始化authenticationManager WebSecurityConfiguration.

在Dave Syer示例中,authenticationManager作为bean公开WebSecurityConfiguration:

    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
Run Code Online (Sandbox Code Playgroud)

然后在AuthorizationServer我们autowire authenticationManager中如下:

    @Autowired
    @Qualifier("authenticationManagerBean")
    private AuthenticationManager authenticationManager;
Run Code Online (Sandbox Code Playgroud)

一旦我这样做了,我终于设法让我的用户通过我的客户JPA用户存储库进行身份验证.