2010-09-12 22 views
7

Hazırda bekletme/JPA2'de Spring Security veritabanı kimlik doğrulamasıyla çalışmak için DAO'ları uygulamaya çalışıyorum.Spring Security Kullanıcısı/Yetkilileri Hazırda Bekletme/JPA2 ile nasıl uygulanır?

alt text

postgresql oluşturmak sorgu olarak repesented: Bahar kullanıcıya & rolleri temsil etmek için aşağıdaki ilişkiler ve dernek kullanan GrantedAuthorities, UserDetailsService ve UserDetailsmanager on-board uygulamaları kullanma

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 
); 

, her şey iyi. Ancak, Spring'in JDBC uygulamasından memnun değilim ve kendi yazımı yazmak istiyorum. Bunu yapmak için, aşağıdaki iş nesneler tarafından ilişkilerin gösterimini oluşturmak için çalıştık:

kullanıcı varlık:

@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 

Ve GrantedAuthorities bir uygulama olarak otorite varlık:

@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 

Sorunum, @ManyToOne Assoc. of AppAuthorities: "kullanıcı adı" olması gerekiyordu, ancak denemek ve bu yüzden bir hata atar, çünkü ben bu özniteliği String olarak tanımlamak zorundayım ... Hibernate ilişkili varlığı bekler. Yani denedim aslında doğru varlık ve @JoinColumn(name = "appuser_fk") tarafından dernek oluşturma. Bu, tabii ki, çöp, Kullanıcı yüklemek için, username içinde yabancı anahtarı olacak, Hibernate her zaman boş olacak appuser_fk içinde arar. Yani burada

sorum şu: veri modelinin doğru JPA2 uygulanmasını almak için yukarıda metioned kodunu değiştirmek konusunda herhangi bir öneri?

Teşekkür

cevap

8

Sen AppAuthority hiç username ihtiyacı yoktur. Spring Security, buna bağlı olamaz çünkü kullanıcı adına erişmek için herhangi bir yöntemi olmayan GrantedAuthority interface'a bağlıdır.

Ama daha iyi uygulama Bahar Security alan adı modeli ayrıştırmak etmektir. Özel bir UserDetailsService olduğunda, ne Spring Security'nin varsayılan veritabanı şemasını ne de nesne modelini taklit etmeniz gerekmez. Sizin UserDetailsService senin AppUser kendi ve AppAuthority yüklemek ve daha sonra bunlara dayanarak UserDetails ve GrantedAuthority s oluşturabilir. Bu, endişelerin daha iyi ayrılmasıyla daha temiz bir tasarıma yol açar.

+0

Ah! Bu harika bir haber! Önerdiğiniz gibi kesinlikle ilerleyeceğim, teşekkürler. –

+0

Proje URL'sini paylaşır mısınız? zaten oauth2 ile bahar önyükleme güvenlik dinlenme api kullanarak bir modül yapmak için 5 gün harcadım. null, "accountNonExpired": false, "accountNonLocked": false, "credentialsNonExpired": false, "etkin": false i url "otoriteler" gibi yanlış veri veriyor çağırdığınızda sorundur. – Harsh

0

Bu alana özgü anahtar kullanarak klasik hazırda sorunu gibi görünüyor. Yeni bir birincil anahtar alanı oluşturmak olası bir düzeltme olacaktır; Örneğin. Users ve Authorities kişiler/tablolar için userId int, Authorities.userName kaldırmak ve benzersiz ikincil anahtara Users.userName değiştirin.

+1

Merhaba. Çözümünüz benim ilk tahmindi ve eğer Spring için olmasaydı, onu kullanırdım (aslında, eğer bu veri modelini tasarlamış olsaydım, * her zaman int int. Kullanmış olurdum). Ancak, tüm bu konfigürasyonda, * kaldırma * özelliklerinin, Spring Security Framework'ün geri kalanı üzerinde öngörülemeyen yan etkilere sahip olabileceğinden korkuyorum. Ya biraz Spring Sec. fasulye Authorities.userName güveniyor? Tabii, onları yeniden uygulayabilirim, ama veri modelimi el değmemiş halde bırakacak başka bir çözüm yok mu? Sonuç olarak, belirli bir veri modeline adapte edilecek bir ORM çerçevesinin beklediğini ve başka bir yoldan değil ... –

0

JPA/hazırda UserDetailsService decouples bir yol daha vardır.

Sen istediğiniz gibi Kullanıcı ve Yetki sınıfını modellemek ve konfigürasyonda userDetailsService tanımlayan bu süre kullanabilirsiniz: -

<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=?" /> 

Bu şekilde size veritabanından kullanıcı ve roller almaya ince ayarlı bir SQL sorgusu tanımlayabilirsiniz. Tek dikkat etmeniz gereken tablo ve sütun ismidir.

İlgili konular