春季安全中错误的定制设计用户/权限

时间:2015-12-15 15:46:25

标签: java spring hibernate jpa spring-security

我认为我的用户和权限设计存在缺陷。登录工作正常,但我的create_user页面上有org.hibernate.TransientPropertyValueException

当我从表单添加新用户时,使用以下代码:

public fr.cnamts.navigo.domain.User create(UserCreateForm form) {
    fr.cnamts.navigo.domain.User user = new fr.cnamts.navigo.domain.User();
    user.setUsername(form.getEmail());
    user.setPassword(new BCryptPasswordEncoder().encode(form.getPassword()));
    user.setEnabled(true);

    Authorities role = new Authorities();
    role.setUser(user);
    role.setAuthority(form.getRole());

    role = authoritiesRepository.save(role);
    user.addAuthorities(role);

    user = userRepository.save(user);
    return user;
}

org.hibernate.TransientPropertyValueException上有一个save(role)。 当局类:

@Entity
@Table(name="authorities", uniqueConstraints = @UniqueConstraint(
        columnNames = { "authority", "username" }))
public class Authorities {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "authority_id", 
    unique = true, nullable = false)
    private Integer id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "username", nullable = false)
    private User user;

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

    public Authorities() {
    }

    public Authorities(User user, String role) {
        this.user = user;
        this.authority = role;
    } 

    public void setUser(User user) {
        this.user = user;
        if (!user.getAuthorities().contains(this)) { // warning this may cause performance issues if you have a large data set since this operation is O(n)
            user.getAuthorities().add(this);
        }
    } // assume other basic getters and setters

用户一:

@Entity
@Table(name = "users")
public class User {
    @Id
    @Column(name = "username", nullable = false, unique = true, length = 60)
    private String username;

    @Column(name = "password", nullable = false, length = 60)
    private String password;

    @Column(name = "enabled", nullable = false)
    private boolean enabled;

    // private boolean tokenExpired;

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "user")
    private Set<Authorities> authorities;

    public User() {
        this.authorities = new HashSet<Authorities>();
    }

    public User(String username, String password, boolean enabled) {
        this.username = username;
        this.password = password;
        this.enabled = enabled;
        this.authorities = new HashSet<Authorities>();
    }

    public User(String username, String password, boolean enabled,
            Set<Authorities> userRoles) {
        this.username = username;
        this.password = password;
        this.enabled = enabled;
        if (userRoles != null) {
            this.authorities = userRoles;
        } else {
            this.authorities = new HashSet<Authorities>();
        }
    }

    public void addAuthorities(Authorities role) {
        this.authorities.add(role);
        if (role.getUser() != this) {
            role.setUser(this);
        }
    }

    public void addAuthorities(String role, User user) {
        this.addAuthorities(new Authorities(user, role));
    }

当我添加新用户时,我需要用户的角色/权限,但是要创建权限对象,我需要用户实例。

以下是控制台跟踪的摘录:

Hibernate: select user0_.username as username1_1_, user0_.enabled as enabled2_1_, user0_.password as password3_1_ from users user0_ where user0_.username=?
Hibernate: select nextval ('hibernate_sequence')
Hibernate: select user_.username, user_.enabled as enabled2_1_, user_.password as password3_1_ from users user_ where user_.username=?
2015-12-15 16:21:26.390  WARN 6832 --- [nio-8081-exec-3] o.h.a.i.UnresolvedEntityInsertActions    : HHH000437: Attempting to save one or more entities that have a non-nullable association with an unsaved transient entity. The unsaved transient entity must be saved in an operation prior to saving these dependent entities.
    Unsaved transient entity: ([fr.cnamts.navigo.domain.User#datm.flux1@cnamts.fr])
    Dependent entities: ([[fr.cnamts.navigo.domain.Authorities#4]])
    Non-nullable association(s): ([fr.cnamts.navigo.domain.Authorities.user])
2015-12-15 16:21:26.398 ERROR 6832 --- [nio-8081-exec-3] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.InvalidDataAccessApiUsageException: org.hibernate.TransientPropertyValueException: Not-null property references a transient value - transient instance must be saved before current operation : fr.cnamts.navigo.domain.Authorities.user -> fr.cnamts.navigo.domain.User; nested exception is java.lang.IllegalStateException: org.hibernate.TransientPropertyValueException: Not-null property references a transient value - transient instance must be saved before current operation : fr.cnamts.navigo.domain.Authorities.user -> fr.cnamts.navigo.domain.User] with root cause

org.hibernate.TransientPropertyValueException: Not-null property references a transient value - transient instance must be saved before current operation : fr.cnamts.navigo.domain.Authorities.user -> fr.cnamts.navigo.domain.User
    at org.hibernate.action.internal.UnresolvedEntityInsertActions.checkNoUnresolvedActionsAfterOperation(UnresolvedEntityInsertActions.java:137) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]

我想我在getter / setter方面或构造方面缺少一些东西。数据库端的对象似乎很好。这是创建代码(postgresql):

create table users ( 
    username varchar(50) primary key,
    password varchar(255) not null,
    enabled boolean not null DEFAULT TRUE
);


create table authorities (
    authority_id SERIAL,
    username varchar(50) not null,
    authority varchar(50) not null,
    PRIMARY KEY (authority_id),
    CONSTRAINT uni_username_authority UNIQUE(authority,username),  
    constraint fk_roles_users foreign key (username) references users (username)
);

1 个答案:

答案 0 :(得分:1)

由于authority表具有users表的外键,因此必须在Authority之前保存User。

您可以让Hibernate在通过向关联添加级联注释来持久保存权限之前保留关联的用户:

@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinColumn(name = "username", nullable = false)
private User user;

https://docs.oracle.com/javaee/5/api/javax/persistence/ManyToOne.html