首页 > 解决方案 > 带有继承的 Spring JWT 身份验证

问题描述

将子类保存到我的数据库后,我遇到了身份验证问题。

我有三个类(Client、RetailClient 和 WholesaleClient),我想按类型注册用户。当我将行保存为客户端(父级)时,一切正常,但是当我想将对象保存为子级(RetailClient、 WholesaleClient)时,我遇到了日志记录问题(401 错误)。是否可以通过继承登录?

我的代码:

家长

@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

孩子

@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

控制器出现 401 错误:

        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);

没有错误的控制器:

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);

自定义用户详细信息服务:

@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);
    }

用户主体:

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
        );
    }

客户端存储库:

@Repository
public interface ClientRepository extends JpaRepository<Client, Long> {
    Optional<Client> findByUsernameOrEmail(String username, String email);

    Boolean existsByUsername(String username);

    Boolean existsByEmail(String email);
}

你能告诉我哪里有问题吗?如果需要更多代码,我可以附上它,但请告诉我哪个类。

数据库中唯一不同的是“类型” - 其他信息是相同的。

我知道我可以将每个变量加入父类,但是可以使用继承登录吗?

编辑 - 日志:

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]

第一个选择是当类型为“RetailClient”时,第二个是当我将其更改为“客户”时,然后有角色声明并且一切正常......

标签: javaspringspring-securityjwt

解决方案


ClientRepository只是在查询Client哪个是父级并且找不到Child。您应该为此编写本机查询。这将帮助您:

@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);

推荐阅读