0
votes

I'm Working on a Spring security project . I try to extends the security.core.userdetails.User class to add more details while registering the users.

User Extended class

public class UserDetails extends User {

    private int id;
    private String Country;


public UserDetails(String username, String password, Collection<? extends GrantedAuthority> authorities, int id,
        String country) {
    super(username, password, authorities);
    this.id = id;
    Country = country;
}

public UserDetails(String username, String password, boolean enabled, boolean accountNonExpired,
        boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities,
        int id, String country) {
    super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);
    this.id = id;
    Country = country;
}




public int getId() {
    return id;
}


public void setId(int id) {
    this.id = id;
}


public String getCountry() {
    return Country;
}


public void setCountry(String country) {
    Country = country;
}

I have also added Id and country in my entity class(model class).

But when i try to register the user . It give an error.org.springframework.dao.DataIntegrityViolationException: PreparedStatementCallback; SQL [insert into users (username, password, enabled) values (?,?,?)]; Field 'id' doesn't have a default value; nested exception is java.sql.SQLException: Field 'id' doesn't have a default value

(The value of id and country is hard coded)

Controller class

try {
        List<GrantedAuthority> authority = new ArrayList<>();
        authority.add(new SimpleGrantedAuthority(form.getRole()));
        String encodedPassword = passwordEncoder.encode(form.getPassword());
        
        UserDetails details = new UserDetails(form.getUsername(), encodedPassword, authority, 10 ,"India");
        System.out.println(details.getId()+" "+details.getCountry() +" "+details.getUsername());
        System.out.println(details);
        detailsManager.createUser(details);
  }

OUPUT

10 India [email protected]

com.example.demo.model.UserDetails [[email protected], Password=[PROTECTED], Enabled=true, AccountNonExpired=true, credentialsNonExpired=true, AccountNonLocked=true, Granted Authorities=[ROLE_ADMIN]]

I don't know why its is calling the parent class constructor.

1
could you add User class definition? - Ismail Durmaz
User is spring security User class( org.springframework.security.core.userdetails.User) - user10260308

1 Answers

0
votes

The SQL is incorrect. Spring Security's INSERT by default populates the username, password, and enabled columns. However, the users table you created requires an id column as well. Since the query doesn't specify the value, it fails.

You could try extending JdbcUserDetailsManager's various methods to be aware of your id field as well. You'd need to at least extend createUser so it adds the id to the INSERT statement and findUserByUsername so it constructs your custom object.

A better way, though, would be to use Spring Data. This allows your domain object to be independent of Spring Security. Also, Spring Data has much broader SQL support.

It might be helpful to call your class something different than a Spring Security interface. So, let's imagine that your custom class (the one with the id) is called YourUser (instead of UserDetails). Now, you can wire a Spring Data-based UserDetailsService to Spring Security like so:

@Service
public class YourUserRepositoryUserDetailsService implements UserDetailsService {
    private final YourUserRepository users; // your Spring Data repository

    // ... constructor

    @Override
    public UserDetails findUserByUsername(String username) {
        YourUser user = this.users.findByUsername(username)
            .orElseThrow(() -> new UserNotFoundException("not found"));
        return new UserDetailsAdapter(user);
    }

    private static class UserDetailsAdapter extends YourUser implements UserDetails {
        UserDetailsAdapter(YourUser user) {
            super(user); // copy constructor
        }

        // ... implement UserDetails methods
    }
}

This UserDetailsService replaces the JdbcUserDetailsManager that you are publishing.