我是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 =
这些是我用来实现自定义用户和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; } }
下面的类是我使用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>
是的,我遇到了同样的问题…想要使用JPA,UserDetailsService但是同样的问题- 找不到用户…最终得到了解决,这要归功于GitHub上Dave Syer的OAuth2示例。
UserDetailsService
问题似乎出在@EnableAuthorizationServer AuthorizationServer类自动连接的authenticationManager实例中。AuthenticationManager是自动连接的,似乎使用默认值初始化DAOAuthenticationProvider,由于某种原因,它不使用自定义JPA,因此UserDetailsService我们使用in初始化了authenticationManager WebSecurityConfiguration。
@EnableAuthorizationServer AuthorizationServer
DAOAuthenticationProvider
WebSecurityConfiguration
在Dave Syer样本中,authenticationManager在Bean中公开为WebSecurityConfiguration:
@Override @Bean public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); }
然后在AuthorizationServer我们自动连接authenticationManager,如下所示:
AuthorizationServer
@Autowired @Qualifier("authenticationManagerBean") private AuthenticationManager authenticationManager;
完成之后,我最终设法根据我的客户JPA用户存储库对我的用户进行身份验证。