我是Spring Boot的新手,我正在尝试配置OAuth 2.0。我现在遇到的问题是,当我尝试请求访问令牌时,我不断收到以下消息:
{ “错误”:“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 用户名= 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;
}
}
下面的类是一个包含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>
答案 0 :(得分:17)
是的,我遇到了同样的问题...想要使用JPA的UserDetailsService
但同样的问题 - 用户无法找到...最终得到了解决,感谢Dave Syer在GitHub上的OAuth2样本。
问题似乎出现在@EnableAuthorizationServer AuthorizationServer
类中自动装配的authenticationManager实例中。 AuthenticationManager在那里自动装配,并且似乎使用默认DAOAuthenticationProvider
初始化,并且由于某种原因它不使用自定义JPA UserDetailsService
我们在WebSecurityConfiguration
中初始化authenticationManager。
在Dave Syer示例中,authenticationManager在WebSecurityConfiguration
:
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
然后在AuthorizationServer
我们按照以下方式自动连接authenticationManager:
@Autowired
@Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
一旦我这样做,我终于设法让我的用户通过我的客户JPA用户存储库进行身份验证。
答案 1 :(得分:1)
我遇到了同样的问题,花了几个小时调查此案。作为解决方法,如果您使用的是Spring Boot版本1.1.8.RELEASE,请将其降级为1.0.2.RELEASE。事情很顺利,但我还没有调查Spring Boot 1.1.8.RELEASE兼容性问题的原因。
答案 2 :(得分:0)
InitializeUserDetailsBeanManagerConfigurer的默认顺序为
static final int DEFAULT_ORDER = Ordered.LOWEST_PRECEDENCE - 5000;
所以它在自定义之前初始化DaoAuthenticationProvider。
@Order(-5001)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { ... }