6

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

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

Spring Boot 控制台中的错误消息说找不到用户。

:使用 org.springframework.security.authentication.dao.DaoAuthenticationProvider 进行身份验证:未找到用户“stromero”:返回单例 bean“authenticationAuditListener”的缓存实例

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

这是 HTTP 请求:

POST /oauth/token HTTP/1.1 主机:localhost:8181 授权:基本 YnJvd3NlcjpzZWNyZXQ= 缓存控制:无缓存 内容类型:application/x-www-form-urlencoded username=stromero&password=password&client_id=browser&client_secret=secret&grant_type=password

这些是我用来实现自定义用户和 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;
}
}

下面的类是一个包含 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;
    }
    }

下面的类是我使用 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());
    }

}

}

下面是我的 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>
4

3 回答 3

17

是的,我有同样的问题......想使用 JPA UserDetailsService,但同样的问题 - 找不到用户......最终得到解决,这要感谢 Dave Syer 在 GitHub 上的 OAuth2 示例。

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

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

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

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

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

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

于 2014-11-17T06:08:40.123 回答
1

我遇到了同样的问题,花了几个小时调查这个案子。作为一种解决方法,如果您使用的是 Spring Boot 版本 1.1.8.RELEASE,请将其降级到 1.0.2.RELEASE。事情进展顺利,但我还没有调查与 Spring Boot 版本 1.1.8.RELEASE 的兼容性问题的原因。

于 2014-11-11T12:10:59.200 回答
1

InitializeUserDetailsBeanManagerConfigurer 的默认顺序为

static final int DEFAULT_ORDER = Ordered.LOWEST_PRECEDENCE - 5000;

所以它在自定义之前初始化了 DaoAuthenticationProvider。

@Order(-5001)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { ... }
于 2018-01-05T14:34:12.187 回答