Spring Data Jpa 使用Spring auditing @CreatedDate持久化实体会产生ClassCastException(String不能强制转换为类UserPrincipal)

xoefb8l8  于 8个月前  发布在  Spring
关注(0)|答案(1)|浏览(85)

新用户无法从我的Web应用的前端注册新帐户,因为persist()函数失败,并出现以下错误:

java.lang.ClassCastException: class java.lang.String cannot be cast to class nathanlively.subalignercss.Models.UserPrincipal (java.lang.String is in module java.base of loader 'bootstrap'; nathanlively.subalignercss.Models.UserPrincipal is in unnamed module of loader 'app')

有趣的是,这个问题不会发生在数据库初始化中,包括持久化一些使用完全相同方法的测试用户。
我已经验证了当我从UserPrincipal中删除“extends BaseEntity”时,错误消失了,因此删除了审计字段。我在StackOverflow上找到了所有的答案,尝试了一系列不同的更改,但都不起作用。
我使用Java 17与PostgreSQL 14.8和Spring 3.1。

SpringSecurityAuditorAware

@Service
public class SpringSecurityAuditorAware implements AuditorAware<Long> {

    @Override
    public @NotNull Optional<Long> getCurrentAuditor() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication == null || !authentication.isAuthenticated()) {
            return Optional.empty();
        }

        Long userId = ((UserPrincipal) authentication.getPrincipal()).getId();
        return Optional.of(userId);
    }
}

AuditorConfig

@Configuration
@EnableJpaAuditing(auditorAwareRef = "springSecurityAuditorAware")
class AuditorConfig {
}

BaseEntity

@Getter
@Setter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseEntity {
    @CreatedBy
    @Column(name = "created_by")
    private Long createdBy;

    @CreatedDate
    @Column(name = "created_date")
    private LocalDateTime createdDate;

    @LastModifiedBy
    @Column(name = "last_modified_by")
    private Long lastModifiedBy;

    @LastModifiedDate
    @Column(name = "last_modified_date")
    private LocalDateTime lastModifiedDate;
}

UserPrincipal

@Entity(name = "UserPrincipal")
@Table(name = "users")
@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class UserPrincipal extends BaseEntity implements UserDetails, Serializable {

    @Serial
    private static final long serialVersionUID = 4935519277851217959L;

    @Id
    @GeneratedValue
    private Long id;

    @Version
    @Column(nullable = false)
    private short version;

    @Email(message = "Please use normal email format: [email protected]")
    @NotBlank(message = "The email must not be blank.")
    @Column(nullable = false, unique = true)
    private String email;

    @NotEmpty(message = "The first name must not be blank.")
    @Column(nullable = false)
    private String firstName;

    @NotBlank(message = "The password must not be blank.")
    @Column(nullable = false)
    @JsonIgnore
    private String password;

    @ManyToOne(optional = false)
    @JoinColumn(name = "authority_id", nullable = false)
    private Authority authority;

    @Builder.Default
    @DecimalMin(value = "300.0", message = "Speed of sound must be between 300 and 400.")
    @DecimalMax(value = "400.0", message = "Speed of sound must be between 300 and 400.")
    @Column(nullable = false)
    private float soundVelocityInMeters = 345.0F;

    @Builder.Default
    private float soundVelocityInFeet = 1133.0F;

    @NotNull
    @Enumerated(EnumType.STRING)
    @Builder.Default
    @Column(name = "user_units", columnDefinition = "units_enum", nullable = false)
    @Type(PostgreSQLEnumType.class)
    private UnitsEnum userUnits = UnitsEnum.METERS;

    @Lob
    private byte[] avatar;

    @Column(name = "avatar_url")
    private String avatarURL;

    @Builder.Default
    private boolean accountNonExpired = true;
    @Builder.Default
    private boolean accountNonLocked = true;
    @Builder.Default
    private boolean credentialsNonExpired = true;
    @Builder.Default
    private boolean enabled = true;

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return Set.of(authority);
    }

    // username = email
    public String getUsername() {
        return email;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || Hibernate.getClass(this) != Hibernate.getClass(o)) return false;
        UserPrincipal that = (UserPrincipal) o;
        return getId() != null && Objects.equals(getId(), that.getId());
    }

    @Override
    public int hashCode() {
        return getClass().hashCode();
    }

}

UserPrincipalRepository

@Transactional(readOnly = true)
@Repository
public interface UserPrincipalRepository extends BaseJpaRepository<UserPrincipal, Long> {
    Optional<UserPrincipal> findByEmailIgnoreCase(String email);

    boolean existsByEmailAllIgnoreCase(String email);
}
lvjbypge

lvjbypge1#

问题出在SpringSecurityAuditorAware上。感谢Jared的提示!

@Service
public class SpringSecurityAuditorAware implements AuditorAware<Long> {

    @Override
    public @NotNull Optional<Long> getCurrentAuditor() {
        try {
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            if (authentication == null || !authentication.isAuthenticated()) {
                return Optional.empty();
            }

            Long userId = ((UserPrincipal) authentication.getPrincipal()).getId();
            return Optional.of(userId);
        } catch (Exception e) {
            return Optional.empty();
        }
    }
}

相关问题