【问题标题】:Spring Security + JPA user schemaSpring Security + JPA 用户模式
【发布时间】:2013-05-01 03:44:12
【问题描述】:

根据 Spring 文档,如果您需要通过数据库管理 Spring 安全性,您应该有一些标准的表模式。例如。

create table users(
    username varchar(256) not null primary key,
    password varchar(256) not null,
    enabled boolean not null
);

create table authorities (
    username varchar(256) not null,
    authority varchar(256) not null,
    constraint fk_authorities_users foreign key(username) references users(username)
);
create unique index ix_auth_username on authorities (username,authority);

我面临的问题如下。 1) 无法理解如何使用 JPA 实现这种表模式?

我尝试了以下方法。

@Entity
@Table(name="USERS")
public class UsersPersistence extends Users implements Serializable{

    private static final long serialVersionUID = 1009548075747154488L;

    public UsersPersistence() {
        super();
    }

    public UsersPersistence(long id, String userName, String password, boolean enabled) {
        super(id, userName,password,enabled);
    }

    @Id
    @GeneratedValue
    @Column(name="id")
    @Override
    public long getId() {
        return super.getId();
    }

    @Column(name="username", nullable=false)
    @Override
    public String getUserName() {
        return super.getUserName();
    }

    @Column(name="password", nullable=false)
    @Override
    public String getPassword() {
        return super.getPassword();
    }

    @Column(name="enabled", nullable=false)
    @Override
    public boolean isEnabled() {
        return super.isEnabled();
    }

}

此表根据 Spring 文档架构中所述的要求创建。 理解的问题是当我试图在权限表中的用户名上分配外键时。由于 JPA 通过父表(主键表)的 id 分配外键,或者我可能不知道如何分配它。

以下是产生问题的 JPA 类:-

@Entity
@Table(name="AUTHORITIES")
public class AuthoritiesPersistence extends Authorities implements Serializable{

    private static final long serialVersionUID = 1L;

    public AuthoritiesPersistence() {
        super();
    }

    public AuthoritiesPersistence(long id, UsersPersistence userName, String authority) {
        super(id,userName,authority);
    }

    @Id
    @GeneratedValue
    @Column(name="id")
    @Override
    public long getId() {
        return super.getId();
    }

    @Override
    @ManyToOne(cascade=CascadeType.ALL)
    @JoinColumn(name="username", nullable=false)
    public UsersPersistence  getUserName() {
        return (UsersPersistence) super.getUserName();
    }

    @Column(name="authority", nullable=false)
    @Override
    public String getAuthority() {
        return super.getAuthority();
    }

}

此表创建成功,但 Spring 安全认证无法识别用户名,因为 JPA 使用外键 id 而不是实际用户名。

任何帮助都将不胜感激。我真的被困在创建一个基于用户名而不是 id 的外键。 谢谢

【问题讨论】:

  • 您的表中没有指定 id。
  • 首先感谢您的快速回复@Kelvin。两个表中都指定了 ID。两个持久性类都扩展了一些父类。这些类有 id 并且在持久性类中我只是覆盖那些 getter 并将它们分配为 table id 列。
  • 但是 ddl 没有提到 id 列
  • 绝对正确。 DDL 没有提到 id 列。那就是问题所在。如果没有 id,我如何分配外键。 JPA 中是否可以根据 DDL 分配这样的外键?或者我没有其他选择,只能手动创建表模式并使用 JDBC dao 而不是 JPA Dao 类?

标签: spring hibernate jpa spring-mvc spring-security


【解决方案1】:

当您使用默认的JdbcDaoImpl 作为UserDetailsService 实现时,您只需遵守Spring Security 参考文档中给出的架构,如果您的安全配置中有<jdbc-user-service> 标记,就是这种情况。即使这样,也可以覆盖用于获取用户和权限的默认 SQL 查询(请参阅namespace appendix)。

但是,如果您使用 hibernate 管理用户帐户,编写自己的 UserDetailsService 实现而不是尝试创建导致 JdbcDaoImpl 所需的特定架构的 JPA 实体是有意义的。

documentation 也表示:

如果您的应用程序确实使用 ORM 工具,您可能更愿意编写自定义 UserDetailsS​​ervice 来重用您可能已经创建的映射文件。

【讨论】:

  • 感谢您的回复@zagyi。根据规范,您不能更改架构。问题从来不在于规范所说的内容。问题是有什么可能的方法可以通过 JPA 实现该模式。
  • 我的意思是说,在你的情况下,忘记使用<jdbc-user-service> 可能更容易,它是默认模式。使用 hibernate 实现您自己的 UserDetailsService 几乎是微不足道的,在任何情况下都不会将其视为 hack 或实现身份验证的非标准方式。
【解决方案2】:

我已经找到了替代方案,即“配置 JdbcUserDetailsManager 以使用自定义 SQL 查询”至少我可以通过 JPA 创建我的表并且可以希望 “ users-by-username-query 和 authority-by-username-query ”确实可以完成我的工作。 为了实现它,我必须添加以下架构。

create table custom_user_authorities (
    id bigint identity,
    user bigint not null,
    authority varchar(256) not null,
);

此架构有 id(将自动递增),它肯定适用于 JPA。

【讨论】:

    【解决方案3】:

    有几种方法可以解决这个问题:

    要使用您感兴趣的方法,您必须知道,除了拥有用户名、密码和启用的 Spring Security 字段之外,还希望用户名是唯一标识符。这意味着您可以使用实体的用户名属性作为数据库和 Hibernate 的 ID。 如果您不想这样做,一种解决方法是设置一个表,其中使用 ID/名称和权限定义权限。然后设置使用可连接将它们映射到用户。 一些未经测试的示例代码: 角色:

    @Entity
    @DynamicUpdate
    @Table(name ="authorities")
    public class Authority{
    
    
        private String authority;
    
        @Id
        @Column(name="authority")
        public String getAuthority() {
            return authority;
        }
    

    用户:

    @Entity
    @DynamicUpdate
    @Table(name = "users", uniqueConstraints={ @UniqueConstraint(columnNames={"username"})})
    public class User {
    private String username;
    private List<Authority> authorities;
    @Type(type = "numeric_boolean")
        private boolean enabled;
    
    
            @Id
        @Column(name="username")
        public String getUsername() {
            return username;
        }
    
            @ManyToMany(fetch = FetchType.EAGER)
        @JoinTable(
           name = "authorities",
           joinColumns = @JoinColumn(name = "username"), 
           inverseJoinColumns = @JoinColumn(name = "rolename")
         )
        public List<Authority> getauthorities() {
            return authorities;
        }
    
            @Column(name="ENABLED")
        public boolean isEnabled() {
            return enabled;
        }
    

    当基础运行时,您可以根据需要添加内部使用的属性。

    【讨论】:

      【解决方案4】:

      我能够使用以下类定义映射这些表:

      @Entity
      @Table(name = "users")
      public class User {
      
          @Id
          @Column(name = "username")
          private String username;
          @Column(name = "password")
          private String password;
          @Column
          private boolean enabled;
          @Column
          private String firstName;
          @ElementCollection
          @JoinTable(name = "authorities", joinColumns = {@JoinColumn(name = "email")})
          @Column(name = "authority")
          private Set<String> roles;
      
          public User() {
          }
      
          public Serializable getId() {
              return username;
          }
      
          public String getUsername() {
              return username;
          }
      
          public String getPassword() {
              return password;
          }
      
          public boolean isEnabled() {
              return enabled;
          }
      
          public void setEnabled(boolean enabled) {
              this.enabled = enabled;
          }
      
          public void setPassword(String password) {
              this.password = password;
          }
      
          public void setUsername(String username) {
              this.username = username;
          }
      
          public String getPassword() {
              return password;
          }
      
          public void setPassword(String password) {
              this.password = password;
          }
      
          public Set<String> getRoles() {
              return roles;
          }
      
          public void setRoles(Set<String> roles) {
              this.roles = roles;
          }
      
      }
      

      【讨论】:

        猜你喜欢
        • 2013-02-13
        • 2012-04-01
        • 2018-03-28
        • 2012-12-15
        • 2012-12-04
        • 2016-02-12
        • 1970-01-01
        • 2014-07-02
        • 2023-04-08
        相关资源
        最近更新 更多