[英]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
);
使用GrantedAuthorities
, UserDetailsService
和UserDetailsmanager
的板載實現,一切都很好。 但是,我對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
可以加載您自己的AppUser
和AppAuthority
,然后根據它們創建UserDetails
和GrantedAuthority
。 這導致更清晰的設計,更好地分離關注點。
還有一種方法可以將UserDetailsService與JPA / Hibernate分離。
您可以根據需要為User和Authority類建模,並在配置中定義userDetailsService時使用它: -
<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問題。 可能的解決方法是創建一個新的主鍵字段; 例如, Users
和Authorities
實體/表的userId int
,刪除Authorities.userName
,並將Users.userName
更改為唯一的輔助密鑰。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.