Spring Boot

  • SQL-Ausgabe auf Log umgelegt
  • Tabellen werden neu angelegt, wenn man die DB löscht
  • FlashScope läuft (anscheinend)
  • eigene Fehlerseite läuft

Inzwischen versuche ich mich in Spring Security reinzudenken.

Das Interface FlashScope hast du aber ziemlich vergewaltigt :verzweifel:
Darüber hinaus ist der Name FlashScope äußerst irreführend. Zumindest im Spring-Kontext erwarte ich da einen “Scope” im Sinne von org.springframework.beans.factory.config.Scope, wie SessionScope oder RequestScope.

Die Controller müssen eigentlich im Singleton-Scope sein, dass der IndexController in der Session liegt, passt irgendwie nicht so ganz. Wieso hast du das gemacht? Die Flash-Attribute, die du mittels RedirectAttributes speicherst, landen auch in der Session, wenn sie vom Singleton-Scope aus gespeichert werden.

Übrigens übernimmt Spring Security für dich die Weiterleitung beim Login / Logout. Dafür brauchst du keinen Controller, weil das über Filter gemacht wird. Optional kannst du einen Controller zur Verfügung stellen, um ein eigenes Loginformular zu rendern.

P. S.: bitte nicht übel nehmen, wenn mir einige Formulierungen nicht so ganz gelungen sind. Die Anmerkungen sollen nicht herablassend klingen - ich versuche nur konstruktive Kritik anzubringen. Und ohne blumige Überleitungen klingt das halt etwas ruppig :wink:

Eigentlich wollte ich die Controller FlashScope implementieren lassen. Bringt aber nichts, da man die statischen Methoden trotzdem nicht “unqualifiziert” (ohne ein FlashScope. davor) ansprechen kann, also habe ich es gelassen. So wie es jetzt ist, könnte ich auch eine Klasse draus machen, stimmt schon.

Darüber hinaus ist der Name FlashScope äußerst irreführend. Zumindest im Spring-Kontext erwarte ich da einen “Scope” im Sinne von org.springframework.beans.factory.config.Scope, wie SessionScope oder RequestScope.

Auch richtig, aber so hieß das Ding eben in Ninja. Ich glaube, ich mache ein enum FlashMessage draus.

Die Controller müssen eigentlich im Singleton-Scope sein, dass der IndexController in der Session liegt, passt irgendwie nicht so ganz. Wieso hast du das gemacht? Die Flash-Attribute, die du mittels RedirectAttributes speicherst, landen auch in der Session, wenn sie vom Singleton-Scope aus gespeichert werden.

Das wusste ich nicht. Habe ein bisschen rumexperimentiert, und es erschien mir logisch. Dann haue ich es wieder raus.

Übrigens übernimmt Spring Security für dich die Weiterleitung beim Login / Logout. Dafür brauchst du keinen Controller, weil das über Filter gemacht wird. Optional kannst du einen Controller zur Verfügung stellen, um ein eigenes Loginformular zu rendern.

Ich hätte mein Loginformular gern auf der Startseite, einfach um den zusätzlichen Klick zu sparen.

P. S.: bitte nicht übel nehmen, wenn mir einige Formulierungen nicht so ganz gelungen sind. Die Anmerkungen sollen nicht herablassend klingen - ich versuche nur konstruktive Kritik anzubringen. Und ohne blumige Überleitungen klingt das halt etwas ruppig :wink:

Nö, überhaupt nicht. Ich bin froh, wenn sich jemand in einigen Spring-Ecken besser auskennt als ich. Bisher habe ich meistens vorhandene Spring-Anwendungen ausgebaut, deshalb ist mein Wissen an den Stellen, wo es ums Aufsetzen und die Konfiguration geht, ziemlich dünn. Dazu kommt noch die Spring-Boot Magic. Du siehst ja auch, dass ich viel herumprobiere, und dabei einiger Mist herauskommt - aber besser jetzt als später.

Das ist kein Problem. Du kannst beliebig viele Controller(-methoden) haben, die ein Loginformular rendern. Wichtig ist nur, dass du die Eingabefelder für den Nutzernamen und das Kennwort sowie die action-URL so setzt, wie sie in Spring Security konfiguriert ist, damit der Interceptor sie korrekt abfängt.
Wohin nach einem (ggf. fehlgeschlagenen) Login weitergeleitet werden soll, kann man auch konfigurieren. Das selbe gilt auch für die Weiterleitung nach einem Logout.

Das ist genau das Wissen, was ich mir in mühseliger Fleißarbeit nach und nach angeeignet habe. Darüber hinaus, insbesondere was “das große Ganze” angeht, habe ich, wie im anderen Thread ja schon erwähnt, nicht sonderlich viel Ahnung.

Mit Spring-Boot habe ich bisher auch noch nicht gearbeitet. Da ich aber spät genug mit Spring angefangen habe, habe ich von Anfang an mit Java-Config gearbeitet, sodass es doch ein paar Parallelen gibt.

Sooo, langsam mache ich Fortschritte mit der Security. Ich orientiere mich ganz grob an Spring Boot Security Application - Bartosz Kielczewski

Wahrscheinlich steht immer noch einiger Blödsinn im Code, aber ich kann erst mal wieder einen Nutzer im Adminbereich anlegen - immerhin.

Ja, langsam verstehe ich das Security-Zeugs.

Jetzt kann sich ein Nutzer (den man vorher über den - aktuell ungesicherten - Admin-Bereich anlegen kann) anmelden und auch wieder abmelden. In den Templates steht er auch zur Verfügung.

Die Details mit den Rollen usw. bekomme ich jetzt herausgefutzelt, und wie man dann die vorhandene Spring Security zum Absichern der App verwendet, weiß ich schon.

Ein Hibernate-Problemchen:

Ich habe jetzt an meinen Nutzern Rollen dranhängen:

@ManyToMany(cascade=CascadeType.ALL)
@JoinTable(name="user_role",
        joinColumns=@JoinColumn(name="user_id"),
        inverseJoinColumns=@JoinColumn(name="role_id"))
private Set<Role> roles;

Die Relationship wird defaultmäßig LAZY aufgelöst, nur bekomme ich dann schon im Service (der den Nutzer nicht über EntityManager, sondern ein JpaRepository bekommt) ein org.hibernate.LazyInitializationException - could not initialize proxy - no Session. Die einzigen beiden Wege, die ich gefunden habe, um das zu vermeiden, ist entweder den Fetch-Typ in @ManyToMany auf EAGER zu stellen, oder die Service-Methode - die ja nur liest - @Transactional zu machen.

Meiner Meinung nach ist das irgendwie beides nicht so der Hit. Irgendwelche Ideen?

Du kannst die Transaktion doch auch readonly deklarieren. Wäre das vielleicht ein Kompromiss?
Für effiziente oder “richtige” Lösungen gibt es hier im Forum andere Experten als mich :wink: (Aus dem Bauch heraus @ThreadPool , @maki , @nillehammer , @Sym , …)

*** Edit ***

Natürlich kommt der Nutzer von einem EntityManager. Der ist in SimpleJpaRepository gekapselt. Oder hättest du mit direktem Zugriff auf den EM eine andere Lösung? Den EM kannst du dir (wie in JEE üblich) mittels PersistenceContext-Annotation in den Service injizieren lassen.

[QUOTE=cmrudolph]Du kannst die Transaktion doch auch readonly deklarieren. Wäre das vielleicht ein Kompromiss?
Für effiziente oder “richtige” Lösungen gibt es hier im Forum andere Experten als mich :wink: (Aus dem Bauch heraus @ThreadPool , @maki , @nillehammer , @Sym , …)[/quote]

Ja, hatte ich übersehen, dass es das gibt. Klingt vernünftig.

Natürlich kommt der Nutzer von einem EntityManager. Der ist in SimpleJpaRepository gekapselt. Oder hättest du mit direktem Zugriff auf den EM eine andere Lösung? Den EM kannst du dir (wie in JEE üblich) mittels PersistenceContext-Annotation in den Service injizieren lassen.

Schon klar, dass das JpaRepository selbst einen EntityManager hat, aber wenn ich ihn direkt im Service injizieren ließe, könnte ich über Annotations dessen Verhalten besser steuern (zumindest habe ich beim Googlen sowas gesehen, via @PersistenceContext(unitName = "myPU", type=PersistenceContextType.EXTENDED)).

Eine Alternative scheinen Join-Fetch-Queries zu sein (die ja auch im JpaRepository angegeben werden können): Four solutions to the LazyInitializationException | uaiHebert (: | Page 7

Fetch joins sind effizienter wenn klar ist was man braucht, Transactional sollte immer an Services, nie an DAO bzw. Repositories.
EM würde ich in DAOs bzw. Repositories packen.

Ich würde es erst mal so lassen: CurrentUserService bekommt ein Readonly-Transactional verpasst, so dass er sich einen security-kompatiblen Usertyp aus “meinem” User zusammenbasteln kann, und dabei auch Zugriff auf die Nutzer-Rollen hat.

Den EM will ich gar nicht sehen, wenn ich es vermeiden kann, sondern möglichst alles über Repositories (u.U. mit Queries) abwickeln.

Da ich angesprochen wurde, will ich kurz meinen Senf dazu geben. Ich muss Euch da etwas enttäuschen. Ihr seid offensichtlich stark auf Spring unterwegs. Das hab ich noch nie benutzt. In einer schlankeren Webanwendung hätte ich zur Vermeidung der LazyInitializationException das Open-EntityManager/Session-In-View Pattern genannt. Also davon ausgehen, dass Entites detached sind, sich einen neuen EntityManager holen und die Entities daran attachen. Schein so zu sein, dass Spring das vor einem verbirgt. Aber bei Spring ist bei mir leider Schluss.

Ansonsten hätte ich nur eine OT-Bemerkung zum Datenmodell. Warum ist die Relation zwischen User und Rolle ManyToMany? Ein User muss doch nur seine Rollen kennen. Warum sollte die Rolle “wissen” welche User sie inne haben. Falls das im Einzelfall nötig sein sollte (evtl. Adminseite mit Übersicht der Rollen), bleibt einem immer noch eine entpsrechende (JPQL-)Query als Ausweg.

@nillehammer Was Spring angeht, gebe ich zu, dass es am Anfang erst einmal ein ganz schöner “Klopper” mit sehr viel “Magie” ist (weswegen ich anfänglich auch auf Ninja gesetzt habe). Dafür gibt es dann aber auch für fast alles und jedes eine Lösung. Und wenn man die Grundstruktur aber erst mal in Sack und Tüten hat, flutscht es ziemlich gut. Spring Data JPA nimmt einem eine Menge ab, und wenn ich mich nicht selbst mit dem EntityManager rumschlagen muss, ist das meiner Meinung nach ein Fortschritt. Die Readonly-Transactions scheinen an der Stelle eine ganz vernünftige Lösung für das Problem zu sein.

Zum Datenmodell: Auch wenn die Beziehung unidirektional ist (also die Rollen nichts von ihren Usern wissen), muss sie trotzdem ManyToMany sein, wenn man mehrere Rollen pro User (und natürlich mehrere User in einer Rolle) erlauben will.

Wollte auch Garnichts gegen Spring sagen, nur, dass ich mich damit nicht auskenne. Bin sicher, dass das von mir genannte Pattern über Spring abbildbar ist. Insofern der Wert meines Postings nur in der Nennung eines Suchbegriffs.

Und was meine OT-Bemerkung angeht: Waren gestern wohl doch zu viele Bier. Mutliplizität und Navigationsrichtung vermischen ist mir doch recht peinlich…

Das unterstützt Spring auch ootb. Dazu muss man nur den OpenEntityManagerInViewInterceptor konfigurieren. Wenn ich das aber richtig überblicke, ist das eigentlich nicht so gut, weil die Session länger als nötig offen bleibt. (Im Service sollte sie allerdings schon noch offen sein, wenn erforderlich. In der View allerdings mMn nicht mehr.)