【问题标题】:spring security deleting users failure春季安全删除用户失败
【发布时间】:2014-10-08 10:50:46
【问题描述】:

我正在使用 Spring 安全性运行 1.1.4.RELEASE spring-boot 应用程序。我有基本的 Spring 用户和权限表和实体:

@Entity
@Table(name = "USERS")
public class User implements UserDetails {

    @Id
    private String username;

    @Column(nullable = false)
    private String password;

    private boolean enabled;
    private boolean accountNonExpired;
    private boolean accountNonLocked;
    private boolean credentialsNonExpired;
    private String companyName;

    @OneToMany(mappedBy = "username", fetch = FetchType.EAGER, orphanRemoval = true)
    @Cascade({org.hibernate.annotations.CascadeType.ALL})
    private Set<Authorities> roles = new HashSet<Authorities>();

/// getters and setters
}


@Entity
public class Authorities {

    @Id
    private String username;
    private String authority;
    /// getters and setters
}

我有一个仓库:

public interface UserRepository extends CrudRepository<User, String> {

    User findUserByUsername(String username);

    void delete(String username);

    List<User> findAll();
}

我用作:

@PreAuthorize("hasRole('ADMIN')")
    @Transactional
    @RequestMapping(value = "/deleteUser/{username}", method = RequestMethod.POST)
    public String deleteUser(@PathVariable String username, Model model) {
        customUserDetailsService.delete(username);

        **the following line throws the exception**
        model.addAttribute(UPLOAD, customUserDetailsService.findAllUsers());
        return "admin";
}

当我尝试删除用户时,出现错误:

org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint ["IX_AUTH_USERNAME: PUBLIC.AUTHORITIES FOREIGN KEY(USERNAME) REFERENCES PUBLIC.USERS(USERNAME) ('testUser')"; SQL statement:
delete from USERS where username=? [23503-172]]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement

我认为我的映射是错误的,但老实说我不知道​​在哪里。

更新: 根据出色的反馈,我现在有以下几点:

@Entity
@Table(name = "USERS")
public class User implements UserDetails {

    @Id
    private String username;

    @Column(nullable = false)
    private String password;

    private boolean enabled;
    private boolean accountNonExpired;
    private boolean accountNonLocked;
    private boolean credentialsNonExpired;
    private String companyName;

    @ElementCollection(fetch=FetchType.EAGER)
    @CollectionTable(name="Authorities", joinColumns=@JoinColumn(name="username"))
    @Column(name="authority")
    private Set<String> roles = new HashSet<String>();

// getters & setters
}

@Repository
public interface UserRepository extends CrudRepository<User, String> {

    User findUserByUsername(String username);

    void delete(String username);

    List<User> findAll();
}

我通过以下方式使用:

@Controller
@PreAuthorize("hasRole('Admin')")
@RequestMapping("/admin")
public class AdminController {

    @PreAuthorize("hasRole('ADMIN')")
    @Transactional
    @RequestMapping(value = "/deleteUser/{username}", method = RequestMethod.POST)
    public String deleteUser(@PathVariable String username, Model model) {
        customUserDetailsService.delete(username);
        updateModelWithAllUsers(model);
        model.addAttribute("user", new User());
        return "admin";
    }

    private void updateModelWithAllUsers(Model model) {
        model.addAttribute(USERLIST, customUserDetailsService.findAllUsers());
    }
..
}

@Component
public class CustomUserDetailsService implements UserDetailsService {
   public void delete(String username) {
        userRepository.delete(username);
    }
..
}

我正在使用 H2,它是我在 liquibase 中创建和播种的:

<createTable tableName="Users">
    <column name="username" type="VARCHAR_IGNORECASE(50)"/>
    <column name="password" type="VARCHAR_IGNORECASE(500)"/>
    <column name="companyName" type="VARCHAR_IGNORECASE(500)"/>
    <column name="enabled" type="boolean"/>
    <column name="accountNonExpired" type="boolean"/>
    <column name="accountNonLocked" type="boolean"/>
    <column name="credentialsNonExpired" type="boolean"/>
</createTable>

<createTable tableName="Authorities">
    <column name="username" type="VARCHAR(50)">
        <constraints primaryKey="true" nullable="false"
                     foreignKeyName="ix_auth_username" references="Users(username)" deleteCascade="true"/>
    </column>
    <column name="authority" type="VARCHAR_IGNORECASE(500)"/>
</createTable>

所以这似乎工作正常。边缘条件是,如果我创建一个用户,删除然后创建一个具有相同用户名的用户,我会得到一个异常:

Caused by: org.h2.jdbc.JdbcSQLException: Unique index or primary key violation: "PRIMARY_KEY_A ON PUBLIC.AUTHORITIES(USERNAME)"; SQL statement:
insert into Authorities (username, authority) values (?, ?) [23505-172]

【问题讨论】:

  • 您是否尝试过先删除用户的权限再删除用户?
  • 是的,我也试过了,但是它会抛出一个 ReferentialIntegrityException 什么的。
  • 要解决问题,请使用delete 的实现以及如何创建表格来更新问题。
  • 我已经用我当前的代码状态更新了这个问题。有什么想法吗?
  • 我对 liquibase 了解不多,但是你好像把主键和外键搞混了。权限表的主键必须跨越用户名和权限。否则,一个用户只能拥有一个角色。

标签: spring-security spring-data-jpa spring-boot


【解决方案1】:

删除Authorities 类。请记住,实体与表不同。在 User 类中使用以下映射:

@ElementCollection(fetch=FetchType.EAGER)
@CollectionTable(name="authorities", joinColumns=@JoinColumn(name="username"))
@Column(name="authority")
private Set<String> authorities;

另外,您可以在外键上添加ON DELETE CASCADE

【讨论】:

  • 不确定我是否关注。如果我没有实体,如何为以下用户保留权限:boldAuthorities authority = new Authorities(user.getUsername(), "USER"); user.getRoles().add(authorities); 粗体如果我确实将权限添加为字符串,这些权限会保留吗?
  • 是的,当用户对象被持久化时,它们会被持久化。
  • 我需要怎么做才能删除用户的权限?我在上面提出了您的建议,并在用户名列中添加了一个约束以删除Cascade="true"。但是当我删除一个用户时,我认为权限也没有被删除(它的 H2 所以我无法直观地验证记录)。
  • 删除用户时,权限表中的相关行应该会自动删除。有了外键(看起来像你的问题),别担心。
  • 如果我创建一个用户,然后删除它,并尝试重新创建相同的用户名,我得到了异常:boldcould not execute statement; SQL [不适用];约束 ["PRIMARY_KEY_A ON PUBLIC.AUTHORITIES(USERNAME)"; SQL 语句:insert into Authorities (username, authority) values (?, ?) [23505-172]];嵌套异常是 org.hibernate.exception.ConstraintViolationException: could not execute stateme bold 所以我认为 Authorities 表记录没有被正确删除
猜你喜欢
  • 2015-12-01
  • 2013-05-28
  • 2013-12-22
  • 2016-11-12
  • 1970-01-01
  • 2014-04-08
  • 2021-01-07
相关资源
最近更新 更多