I have been trying to enable a form-based user registration in my project.
Just registering a user with no roles works fine, however adding some roles gives me an error:
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of
org.springframework.security.core.GrantedAuthority(no Creators, like default construct, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information at [Source: (PushbackInputStream); line: 9, column: 21] (through reference chain: com.warehousing.model.User["authorities"]->java.util.HashSet[0])
I suspect that the problem is coming from the fact that Spring has no idea how to convert from my authority representation to something that extends GrantedAuthority. That being said, I tried switching from GrantedAuthority to SimpleGrantedAuthority, but the problem remained.
My User class looks like follows:
public class User implements UserDetails {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String fName;
private String lName;
private String phone;
private String email;
// Properties for Spring authentication
@ElementCollection(targetClass=SimpleGrantedAuthority.class, fetch = FetchType.EAGER)
@JsonDeserialize(as = SimpleGrantedAuthority.class)
private Set<? extends GrantedAuthority> grantedAuthorities;
@NotNull
private String password;
@NotNull
private String username;
private boolean isAccountNonExpired;
private boolean isAccountNonLocked;
private boolean isCredentialsNonExpired;
private boolean isEnabled;
public User(String fName, String lName, String phone, String email,
Set<SimpleGrantedAuthority> grantedAuthorities, String password, String username,
boolean isAccountNonExpired, boolean isAccountNonLocked,
boolean isCredentialsNonExpired, boolean isEnabled) {
this.fName = fName;
this.lName = lName;
this.phone = phone;
this.email = email;
this.grantedAuthorities = grantedAuthorities;
this.password = password;
this.username = username;
this.isAccountNonExpired = isAccountNonExpired;
this.isAccountNonLocked = isAccountNonLocked;
this.isCredentialsNonExpired = isCredentialsNonExpired;
this.isEnabled = isEnabled;
}
// Getters, setters & constructors
}
My UserController`s method to create a new user:
@RestController
public class UserController {
@Autowired
UserService userService;
@PostMapping("/users")
public User createNew(@Valid @RequestBody User user) {
return userService.addUser(user);
}
}
Which calls the UserService:
public class UserService implements UserDetailsService {
@Autowired
UserRepository userRepo;
public User addUser(User user) {
Set<SimpleGrantedAuthority> userRoles = user.getGrantedAuthorities()
.stream()
.map(p -> new SimpleGrantedAuthority(p.getAuthority()))
.collect(Collectors.toSet());
User newUser = new User(user.getfName(), user.getlName(),
user.getPhone(), user.getEmail(),
userRoles,
user.getPassword(), user.getUsername(),
user.isAccountNonExpired(),
user.isAccountNonLocked(),
user.isCredentialsNonExpired(),
user.isEnabled());
return userRepo.save(newUser);
}
}
Now, posting a JSON like this works fine:
{
"fName": "First",
"lName": "Last",
"phone": "+3111111111",
"email": "test@mail.com",
"password": "password",
"username": "user",
"enabled": true,
"authorities": [],
"accountNonLocked": true,
"accountNonExpired": true,
"credentialsNonExpired": true
}
However, adding some kind of authority will cause the error mentioned above:
{
"fName": "First",
"lName": "Last",
"phone": "+3111111111",
"email": "test@mail.com",
"password": "password",
"username": "user",
"enabled": true,
"authorities": [{
"authority": "inventory:read"
}],
"accountNonLocked": true,
"accountNonExpired": true,
"credentialsNonExpired": true
}
Any kind of help is appreciated!