首页 > 解决方案 > 通过 @EmbeddedID 上的持久属性 [model.Credentials#email] 的反射访问字段时出错

问题描述

Authorities在我的 Spring Boot 应用程序上实施时遇到了问题,经过一番挖掘后,我意识到我CredentialsAuthorities表之间的关联可能是错误的。

事实上,我注意到 Spring 允许每种类型的用户(无论他们的权限如何)访问任何方法,即使是我认为已经安全的方法。那时,我实现了一个.findAll()方法来实际查看关联是否存在某种问题,并且确实存在。

让我先展示一下 MySQL 表:

CREATE TABLE credentials (
  credential_id bigint UNSIGNED PRIMARY KEY AUTO_INCREMENT,
  email varchar(50) NOT NULL UNIQUE,
  password varchar(255) NOT NULL,
  enabled BOOLEAN NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;


CREATE TABLE authorities (
  email varchar(50) NOT NULL,
  authority varchar(50) NOT NULL,
  PRIMARY KEY (email, authority),
  CONSTRAINT fk_authorities_credentials FOREIGN KEY(email) REFERENCES credentials(email)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

在跳转到实体中的关联之前,值得注意的是,权限实体具有反映表设计的嵌入键。因此,该email列同时是 aforeign key和主要的:

@Embeddable
public class AuthoritiesKey implements Serializable {
  @OneToOne(fetch = FetchType.LAZY)
  @JoinColumn(name = "email", nullable = false
          ,referencedColumnName = "email")
  private Credentials credentials;

  @JoinColumn(name = "authority", nullable = false)
  private String authority;

//getters setters omitted
}

凭证类:

@Entity
@Table(name = "credentials")
public class Credentials implements Serializable {

  @OneToOne(cascade = CascadeType.ALL,
          fetch = FetchType.LAZY,
          mappedBy = "ak.credentials")
  private Authorities authorities;

//other fields and getters/setters omitted
}

删除双边关联(因此只保留在 AuthoritiesKey 类上),hibernate 成功返回所有凭据(当然没有他们的权限)和所有权限(这次是在凭据之前)。

我无法绕过它。

当我离开双边关联时,我得到的完整有意义的堆栈跟踪如下:

Error accessing field [private java.lang.String com.server.model.Credentials.email] by reflection for persistent property [com.servermodel.Credentials#email] : 64; 
nested exception is org.hibernate.property.access.spi.PropertyAccessException: Error accessing field [private java.lang.String com.server.model.Credentials.email] by reflection for persistent property [com.server.model.Credentials#email] : 64

其中 64 是表中(最后一个,但被称为第一个)行的 id Credential

=== 更新 ===

public class CredentialsService implements UserDetailsService {

  @Autowired
  private CredentialsRepository cr;

   @Override
  public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
    Credentials user = cr.findByEmail(email);
    if (user == null){
      throw new UsernameNotFoundException(email);
    }
    return new UserPrincipal(user);
  }
}

标签: javaspring-boothibernatespring-securityspring-data-jpa

解决方案


@OneToOne由于休眠错误,您似乎无法在此处使用。(请参阅HHH-3824)即使使用 Hibernate 5.4.28.Final 也可以复制它

作为解决方法,我建议您以这种方式更正您的映射:

@Entity
@Table(name = "authorities")
public class Authorities {
   
   @EmbeddedId
   private AuthoritiesKey pk;

   // ...
}

@Embeddable
public class AuthoritiesKey implements Serializable {

  @ManyToOne(fetch = FetchType.LAZY)
  @JoinColumn(name = "email", referencedColumnName = "email", nullable = false)
  private Credentials credentials;

  @Column(name = "authority", nullable = false)
  private String authority;

  public AuthoritiesKey(Credentials credentials, String authority) {
    this.credentials = credentials;
    this.authority = authority;
  }

  public AuthoritiesKey() {
  }

  // getters setters omitted
  
  @Override
  public boolean equals(Object o) {
    if ( this == o ) return true;
    if ( o == null || getClass() != o.getClass() ) return false;

    AuthoritiesKey pk = (AuthoritiesKey) o;
    return Objects.equals( credentials, pk.credentials ) &&
            Objects.equals( authority, pk.authority );
  }

  @Override
  public int hashCode() {
    return Objects.hash( credentials, authority );
  }
}

@Entity
@Table(name = "credentials")
public class Credentials implements Serializable {
   
   @Id
   @Column(name = "credential_id")
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   private Long id;

   @OneToMany(cascade = CascadeType.ALL,
        fetch = FetchType.LAZY,
        mappedBy = "pk.credentials")
   private List<Authorities> authorities;

   @NaturalId
   @Column(name = "email")
   private String email;

   @Transient
   public Authorities getAuthority()
   {
      return this.authorities != null && this.authorities.size() > 0
          ? this.authorities.get(0) : null;
   }
 
   // ...
}

推荐阅读