Unable to populate User object when using Spring OAuth2 Jdbc token store
I have updated Roy Clarkson Spring REST service ( https://github.com/royclarkson/spring-rest-service-oauth ) with Jdbc based token store. The original implementation uses an in-memory token store. I was able to see the user details in the User object. On the other hand, after switching to Jdbc based token store, all fields in the User object were empty. It seems that Spring Security was not able to bind an access token to the user I got the token under when I was using the Jdbc based token store.
Implementing an in-memory token store:
@Configuration
@EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends
AuthorizationServerConfigurerAdapter {
@Autowired
private DataSource dataSource;
private TokenStore tokenStore = new InMemoryTokenStore();
@Autowired
@Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
@Autowired
private CustomUserDetailsService userDetailsService;
@Autowired
private ClientDetailsService clientDetailsService;
@Bean
public ClientDetailsService clientDetailsService() {
return new JdbcClientDetailsService(dataSource);
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
// @formatter:off
endpoints
.tokenStore(this.tokenStore)
.authenticationManager(this.authenticationManager)
.userDetailsService(userDetailsService);
// @formatter:on
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.withClientDetails(clientDetailsService);
}
@Bean
@Primary
public DefaultTokenServices tokenServices() {
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setSupportRefreshToken(true);
tokenServices.setTokenStore(this.tokenStore);
return tokenServices;
}
}
REST endpoint:
@RequestMapping("/greeting")
public Greeting greeting(@AuthenticationPrincipal User user) {
return new Greeting(counter.incrementAndGet(), String.format(template, user.getName()));
}
user.getName () returns the username where I got the access token.
Jdbc token store implementation:
@Configuration
@EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends
AuthorizationServerConfigurerAdapter {
@Autowired
private DataSource dataSource;
@Autowired
private TokenStore tokenStore;
@Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
@Autowired
@Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
@Autowired
private CustomUserDetailsService userDetailsService;
@Autowired
private ClientDetailsService clientDetailsService;
@Bean
public ClientDetailsService clientDetailsService() {
return new JdbcClientDetailsService(dataSource);
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
// @formatter:off
endpoints
.tokenStore(this.tokenStore)
.authenticationManager(this.authenticationManager)
.userDetailsService(userDetailsService);
// @formatter:on
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.withClientDetails(clientDetailsService);
}
@Bean
@Primary
public DefaultTokenServices tokenServices() {
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setSupportRefreshToken(true);
tokenServices.setTokenStore(this.tokenStore);
return tokenServices;
}
}
REST endpoint:
@RequestMapping("/greeting")
public Greeting greeting(@AuthenticationPrincipal User user) {
return new Greeting(counter.incrementAndGet(), String.format(template, user.getName()));
}
user.getName () returns null.
CustomUserDetailsService
@Service
public class CustomUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
@Autowired
public CustomUserDetailsService(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByLogin(username);
if (user == null) {
throw new UsernameNotFoundException(String.format("User %s does not exist!", username));
}
return new UserRepositoryUserDetails(user);
}
private final static class UserRepositoryUserDetails extends User implements UserDetails {
private static final long serialVersionUID = 1L;
private UserRepositoryUserDetails(User user) {
super(user);
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return getRoles();
}
@Override
public String getUsername() {
return getLogin();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
}
User
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
@NotEmpty
private String name;
@NotEmpty
@Column(unique = true, nullable = false)
private String login;
@NotEmpty
private String password;
@NotEmpty
private String privilege;
@JsonIgnore
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "user_role", joinColumns = { @JoinColumn(name = "user_id") }, inverseJoinColumns = { @JoinColumn(name = "role_id") })
private Set<Role> roles = new HashSet<Role>();
public User() {
}
public User(User user) {
super();
this.id = user.getId();
this.name = user.getName();
this.login = user.getLogin();
this.password = user.getPassword();
this.roles = user.getRoles();
this.privilege = user.getPrivilege();
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLogin() {
return login;
}
public void setLogin(String login) {
this.login = login;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getPrivilege() {return privilege; }
public void setPrivilege(String privilege) {this.privilege = privilege; }
public Set<Role> getRoles() {
return roles;
}
public void setRoles(Set<Role> roles) {
this.roles = roles;
}
}
source to share
The problem is that the "UserRepositoryUserDetails" you are creating is not serializable.
UserRepositoryUserDetails implements "UserDetails", which is Serializable, but the class it extends "User" is not Serializable.
You must also get a compiler warning to add the serialId.
Decision
Make your UserRepositoryUserDetails serializable.
source to share