簡體   English   中英

如何使用Hibernate / JPA2實現Spring Security用戶/權限?

[英]Howto implement Spring Security User/Authorities with Hibernate/JPA2?

我正在嘗試實現DAO以在Hibernate / JPA2中使用Spring Security數據庫身份驗證。 Spring使用以下關系和關聯來表示用戶和角色:

替代文字

作為postgresql創建查詢重復:

CREATE TABLE users
(
  username character varying(50) NOT NULL,
  "password" character varying(50) NOT NULL,
  enabled boolean NOT NULL,
  CONSTRAINT users_pkey PRIMARY KEY (username)
);
CREATE TABLE authorities
(
  username character varying(50) NOT NULL,
  authority character varying(50) NOT NULL,
  CONSTRAINT fk_authorities_users FOREIGN KEY (username)
      REFERENCES users (username) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION
);

使用GrantedAuthoritiesUserDetailsServiceUserDetailsmanager的板載實現,一切都很好。 但是,我對Spring的JDBC實現不滿意,並且想編寫自己的實現。 為此,我嘗試通過以下業務對象創建關系的表示:

用戶實體:

@Entity
@Table(name = "users", uniqueConstraints = {@UniqueConstraint(columnNames = {"username"})})
public class AppUser implements UserDetails, CredentialsContainer {

    private static final long serialVersionUID = -8275492272371421013L;

    @Id
    @Column(name = "username", nullable = false, unique = true)
    private String username;

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

    @OneToMany(
            fetch = FetchType.EAGER, cascade = CascadeType.ALL,
            mappedBy = "appUser"
    )
    private Set<AppAuthority> appAuthorities;

    @Column(name = "accountNonExpired")
    private Boolean accountNonExpired;

    @Column(name = "accountNonLocked")
    private Boolean accountNonLocked;

    @Column(name = "credentialsNonExpired")
    private Boolean credentialsNonExpired;

    @OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @JoinColumn(name = "personalinformation_fk", nullable = true)
    @JsonIgnore
    private PersonalInformation personalInformation;

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

    public AppUser(
            String username,
            String password,
            boolean enabled,
            boolean accountNonExpired,
            boolean credentialsNonExpired,
            boolean accountNonLocked,
            Collection<? extends AppAuthority> authorities,
            PersonalInformation personalInformation
    ) {
        if (((username == null) || "".equals(username)) || (password == null)) {
            throw new IllegalArgumentException("Cannot pass null or empty values to constructor");
        }

        this.username = username;
        this.password = password;
        this.enabled = enabled;
        this.accountNonExpired = accountNonExpired;
        this.credentialsNonExpired = credentialsNonExpired;
        this.accountNonLocked = accountNonLocked;
        this.appAuthorities = Collections.unmodifiableSet(sortAuthorities(authorities));
        this.personalInformation = personalInformation;
    }

    public AppUser() {
    }

    @JsonIgnore
    public PersonalInformation getPersonalInformation() {
        return personalInformation;
    }

    @JsonIgnore
    public void setPersonalInformation(PersonalInformation personalInformation) {
        this.personalInformation = personalInformation;
    }

    // Getters, setters 'n other stuff

而權威實體作為GrantedAuthorities的實現:

@Entity
@Table(name = "authorities", uniqueConstraints = {@UniqueConstraint(columnNames = {"id"})})
public class AppAuthority implements GrantedAuthority, Serializable {
    //~ Instance fields ================================================================================================

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.TABLE)
    @Column(name = "id", nullable = false)
    private Integer id;

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

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

    // Here comes the buggy attribute. It is supposed to repesent the
    // association username<->username, but I just don't know how to
    // implement it 
    @ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @JoinColumn(name = "appuser_fk")
    private AppUser appUser;

    //~ Constructors ===================================================================================================

    public AppAuthority(String username, String authority) {
        Assert.hasText(authority,
                "A granted authority textual representation is required");
        this.username = username;
        this.authority = authority;
    }

    public AppAuthority() {
    }

    // Getters 'n setters 'n other stuff

我的問題是@ManyToOne assoc。 AppAuthorities :它應該是“用戶名”,但嘗試和這樣做會引發錯誤,因為我必須將該屬性表示為String ...而Hibernate期望關聯的實體。 所以我嘗試的實際上是提供正確的實體並通過@JoinColumn(name = "appuser_fk")創建關聯。 這當然是垃圾,因為為了加載用戶,我將在username使用外鍵,而Hibernate在appuser_fk搜索它,它始終為空。

所以這是我的問題:關於如何修改上面提到的代碼以獲得數據模型的正確JPA2實現的任何建議?

謝謝

AppAuthority根本不需要username Spring Security不能依賴它,因為它依賴於GrantedAuthority接口 ,該接口沒有任何方法來訪問用戶名。

但更好的做法是將您的域模型與Spring Security分離。 當您擁有自定義UserDetailsService ,您不需要模仿Spring Security的默認數據庫模式或其對象模型。 您的UserDetailsService可以加載您自己的AppUserAppAuthority ,然后根據它們創建UserDetailsGrantedAuthority 這導致更清晰的設計,更好地分離關注點。

還有一種方法可以將UserDetailsS​​ervice與JPA / Hibernate分離。

您可以根據需要為User和Authority類建模,並在配置中定義userDetailsS​​ervice時使用它: -

<sec:jdbc-user-service data-source-ref="webDS"
                id="userDetailsService"
                users-by-username-query="SELECT USER_NAME,PASSWORD,TRUE FROM CUSTOMER WHERE USER_NAME=?"
                authorities-by-username-query="SELECT c.USER_NAME,r.ROLE_NAME from CUSTOMER c 
                                          JOIN CUSTOMER_ROLE cr ON c.CUSTOMER_ID = cr.CUSTOMER_ID 
                                          JOIN ROLE r ON cr.ROLE_ID = r.ROLE_ID 
                                          WHERE USER_NAME=?" />

這樣,您可以定義微調SQL查詢以從數據庫中獲取用戶和角色。 您需要注意的是表和列名稱。

這看起來像使用特定於域的密鑰的經典Hibernate問題。 可能的解決方法是創建一個新的主鍵字段; 例如, UsersAuthorities實體/表的userId int ,刪除Authorities.userName ,並將Users.userName更改為唯一的輔助密鑰。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM