简体   繁体   中英

Spring JWT authentication with inheritance

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM