4

我是Spring Boot的新手,我试图配置OAuth 2.0。我在这个时刻遇到的问题是,我不断收到以下消息时,我试图请求访问令牌:春季启动OAuth 2.0 UserDetails用户未找到

{ “错误”:“invalid_grant”, “ERROR_DESCRIPTION”:“坏凭据“ }

Spring Boot控制台中的错误消息表示无法找到用户。

:使用org.springframework.security.authentication.dao.DaoAuthenticationProvider 验证尝试:用户“stromero”未找到 :单豆“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); 
} 

下面是我创建

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

下面的类使用我的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> 
+0

我看不到任何东西(您确定用户在数据库中,并且您正在连接到该数据库)?尽管你的代码中有些奇怪的东西(其中没有一个会导致你的症状):1.存储库意味着主键是一个“String”,但“@ Entity”具有“Long”; 2.我不明白联合用户/客户详细信息服务; 3.您似乎尝试使匿名用户可以使用/令牌端点; 4.您在表单参数中使用客户端凭据,但您尚未告知auth服务器要执行此操作。也许你可以分享一个完整的项目? – 2014-10-07 06:03:09

+0

感谢您的回应,我测试了资源库,并且能够检索用户,据说我将使用long作为主键。我将努力摆脱综合服务。我不知道如何处理3和4.我不能共享整个项目,但是我已经基于以下示例的很多源代码,请让我知道这是否有帮助。 https://github.com/juleswhite/mobilecloud-14/tree/master/examples/9-VideoServiceWithOauth2正如我之前所说的JPA部分似乎没有问题。我试图实现的OAuth 2.0流程是密码授予。 – 2014-10-07 12:28:41

+0

这也可能提供更多的上下文 - > https://www.youtube.com/watch?v=KmCvyg20xi4 – 2014-10-07 13:03:11

回答

16

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

该问题似乎是在@EnableAuthorizationServer AuthorizationServer类中的authenticationManager实例中自动装配的。 AuthenticationManager在那里自动装配,并且似乎使用默认DAOAuthenticationProvider进行初始化,由于某种原因,它不使用自定义JPA UserDetailsService,我们初始化了身份验证管理器WebSecurityConfiguration

在戴夫Syer样品的AuthenticationManager公开为豆在WebSecurityConfiguration

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

然后在AuthorizationServer我们自动装配的AuthenticationManager如下:

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

一旦我做到了,我终于成功地让我的用户通过我的客户JPA用户存储库进行身份验证。

+0

感谢您的回答,我以同样的方式实现了AuthenticationManager,并能够让自定义JPA用户存储库毫无问题地工作。 – 2014-11-17 20:12:55

1

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

0

InitializeUserDetailsBeanManagerConfigurer定制一个前默认顺序为

static final int DEFAULT_ORDER = Ordered.LOWEST_PRECEDENCE - 5000; 

所以Initializee DaoAuthenticationProvider的时候。

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