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 { ... }