I have problem with authentication after saving child class to my database.
I have three classes (Client, RetailClient and WholesaleClient) and I want to register user by type. When I save row as Client (parent) everything works but when I want to save object as child (RetailClient, WholesaleClient) I have problem with logging (401 error). Is it possible to sign in with inheritance?
My code:
Parent
@Entity
@Table(name = "clients", uniqueConstraints = {
@UniqueConstraint(columnNames = {
"username"
}),
@UniqueConstraint(columnNames = {
"email"
})
})
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(
name = "Type",
discriminatorType = DiscriminatorType.STRING
)
public class Client extends DateAudit {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotBlank
@Size(max = 40)
private String name;
@NotBlank
@Size(max = 15)
private String username;
@NaturalId
@NotBlank
@Size(max = 40)
@Email
private String email;
@NotBlank
@Size(max = 100)
private String password;
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "user_roles",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "role_id"))
private Set<Role> roles = new HashSet<>();
public Client() {
}
public Client(String name, String username, String email, String password) {
this.name = name;
this.username = username;
this.email = email;
this.password = password;
}
... getters and setters
Child
@Entity
@DiscriminatorValue("RetailClient")
public class RetailClient extends Client {
@Column(name = "First_Name")
private String firstName;
@Column(name = "Second_Name")
private String secondName;
public RetailClient(String firstName, String secondName) {
this.firstName = firstName;
this.secondName = secondName;
}
... getters and setters
Controller with 401 error:
RetailClient retailClient = new RetailClient("TestImie", "TestNazwisko");
retailClient.setName(signUpRequest.getName());
retailClient.setUsername(signUpRequest.getUsername());
retailClient.setEmail(signUpRequest.getEmail());
retailClient.setPassword(signUpRequest.getPassword());
retailClient.setPassword(passwordEncoder.encode(retailClient.getPassword()));
Role clientUserRole = roleRepository.findByName(RoleName.ROLE_USER)
.orElseThrow(() -> new AppException("User Role not set."));
retailClient.setRoles(Collections.singleton(clientUserRole));
Client result = clientRepository.save(retailClient);
Controller with no error:
Client client1 = new Client(signUpRequest.getName(), signUpRequest.getUsername(),
signUpRequest.getEmail(), signUpRequest.getPassword());
client1.setPassword(passwordEncoder.encode(client1.getPassword()));
Role userRole = roleRepository.findByName(RoleName.ROLE_USER)
.orElseThrow(() -> new AppException("User Role not set."));
client1.setRoles(Collections.singleton(userRole));
Client result = clientRepository.save(client1);
CustomUserDetailService:
@Service
public class CustomUserDetailsService implements UserDetailsService {
private ClientRepository clientRepository;
public CustomUserDetailsService(ClientRepository clientRepository) {
this.clientRepository = clientRepository;
}
@Override
@Transactional
public UserDetails loadUserByUsername(String usernameOrEmail)
throws UsernameNotFoundException {
Client client = clientRepository.findByUsernameOrEmail(usernameOrEmail, usernameOrEmail)
.orElseThrow(() ->
new UsernameNotFoundException("User not found with username or email : " + usernameOrEmail)
);
return UserPrincipal.create(client);
}
@Transactional
public UserDetails loadUserById(Long id) {
Client client = clientRepository.findById(id).orElseThrow(
() -> new ResourceNotFoundException("User", "id", id)
);
return UserPrincipal.create(client);
}
UserPrincipal:
public class UserPrincipal implements UserDetails {
private Long id;
private String name;
private String username;
@JsonIgnore
private String email;
@JsonIgnore
private String password;
private Collection<? extends GrantedAuthority> authorities;
public UserPrincipal(Long id, String name, String username, String email, String password, Collection<? extends GrantedAuthority> authorities) {
this.id = id;
this.name = name;
this.username = username;
this.email = email;
this.password = password;
this.authorities = authorities;
}
public static UserPrincipal create(Client client) {
List<GrantedAuthority> authorities = client.getRoles().stream().map(role ->
new SimpleGrantedAuthority(role.getName().name())
).collect(Collectors.toList());
return new UserPrincipal(
client.getId(),
client.getName(),
client.getUsername(),
client.getEmail(),
client.getPassword(),
authorities
);
}
ClientRepository:
@Repository
public interface ClientRepository extends JpaRepository<Client, Long> {
Optional<Client> findByUsernameOrEmail(String username, String email);
Boolean existsByUsername(String username);
Boolean existsByEmail(String email);
}
Could you tell me where can be a problem? If more code is needed I can attach it but please tell me which class.
The only thing different in database is "Type" - other info is the same.
I know that I can join every variable to parent class but is it possible to sign in with inheritance?
EDIT - LOGS:
2018-12-10 22:41:41.241 DEBUG 9499 --- [nio-8080-exec-3] org.hibernate.SQL : select * from clients c where c.username = ? or c.email = ?
2018-12-10 22:41:41.242 TRACE 9499 --- [nio-8080-exec-3] o.h.type.descriptor.sql.BasicBinder : binding parameter [2] as [VARCHAR] - [thomas]
2018-12-10 22:41:41.242 TRACE 9499 --- [nio-8080-exec-3] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [VARCHAR] - [thomas]
2018-12-10 22:41:41.248 DEBUG 9499 --- [nio-8080-exec-3] org.hibernate.SQL : select roles0_.user_id as user_id1_15_0_, roles0_.role_id as role_id2_15_0_, role1_.id as id1_14_1_, role1_.name as name2_14_1_ from user_roles roles0_ inner join roles role1_ on roles0_.role_id=role1_.id where roles0_.user_id=?
2018-12-10 22:41:41.249 TRACE 9499 --- [nio-8080-exec-3] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [BIGINT] - [1]
2018-12-10 22:41:49.718 DEBUG 9499 --- [nio-8080-exec-4] org.hibernate.SQL : select * from clients c where c.username = ? or c.email = ?
2018-12-10 22:41:49.718 TRACE 9499 --- [nio-8080-exec-4] o.h.type.descriptor.sql.BasicBinder : binding parameter [2] as [VARCHAR] - [anthony]
2018-12-10 22:41:49.718 TRACE 9499 --- [nio-8080-exec-4] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [VARCHAR] - [anthony]
First select is when type is 'RetailClient' and second is when I change it to 'Client', after that there is roles statement and everything is working...
Your ClientRepository
is just querying over Client
which is parent and would not found Child
. You should write a native query for that. This will helps you:
@Query(value = "select * from clients c where c.username = :username or c.email = :email", nativeQuery = true)
Optional<Client> findByUsernameOrEmail(@Param("username") String username, @Param("email") String email);
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.