Communardo Software GmbH, Kleiststraße 10 a, D-01129 Dresden

Session Handling in Grails

Dieser Artikel soll einen Überblick über das Session Objekt in Grails geben und Lösungsmöglichkeiten bei Anwendungsfehlern aufzeigen.

Die all­gegenwärtige Session

In allen Controllern ist die Session bereits stan­dard­mä­ßig im Application-Scope und war­tet gebrauchs­fer­tig mit dem Namen "ses­sion" auf Verwendung. Das Objekt imple­men­tiert im wesent­li­chen die Standardfunktionalität der javax.servlet.HttpSession und hält ergän­zend die Zugriffsmöglichkeiten einer GroovyMap bereit.

session.username = 'John Doe'

println session["username"] oder
println session.username

Das Objekt selbst wird im Hintergrund über request.getSession() initia­li­siert, daher hat man auch nach einem session.invalidate() bzw. einem Timeout beim nächs­ten Actionaufruf (Achtung: Nicht im sel­ben Controller!) eine neue Session. Am Beispiel einer Nutzer-Authentifizierung soll der Zugriff ver­deut­lich werden:

//Im Interceptor ist die Authentifizierung defi­niert, die vor jeder Action aus­ge­führt wer­den soll
def beforeInterceptor = [action:this.&auth,except:'login']

//Falls kein Nutzer in der Session gefun­den wird, erfolgt eine Weiterleitung zur Login-Action
def auth() {
if(!session.user) {
redirect(action:'login')
return false
}
}
def login = {
// zeige Login-Seite
}

Session Timeouts

Wie oben bereits ange­spro­chen, muss man sich kein neues Session Objekt nach einem Timeout besor­gen. Leider hat die­ser Mechanismus aber den Nachteil, dass man dadurch nicht merkt ob eine neue Session durch den Timeout einer Vorhergehenden erzeugt wurde und Rückschlüsse dar­auf las­sen nur eine neue ID und die feh­len­den Session-Attribute zu.

Braucht ein Nutzer z.B. zu lange um eine Formular aus­zu­fül­len, bekommt er beim nächs­ten Klick den Zugriff ver­wei­gert oder der aktu­elle Bearbeitungssatus wird zurück­ge­setzt. Sicher ist dies in den meis­ten Fällen eines Session-Timeouts der Fall, aber hier ist der unbe­rech­tigte Zugriff mit dem Timeout gleich­ge­setzt und der Nutzer kann schlecht expli­zit auf eine abge­lau­fene Session hin­ge­wie­sen werden.

Eine Möglichkeit her­aus­zu­fin­den ob die aktu­elle Session noch exis­tiert und eine ent­spre­chende Meldung aus­zu­ge­ben, wäre einen Parameter zu set­zen und die­sen vor einer Action abzufragen:

sta­tic beforeInterceptor = [action:this&checkSession, except:'login']

def checkSession = {
if(!session.timeout)
render("Session Timeout!")
}

AfterInterceptor Verwendung

Die Möglichkeiten eines Interceptors sind viel­fäl­tig, soll­ten aber gerade bei Ausführung nach Methodenaufrufen mit bedacht ein­ge­setzt werden.

Beispiel: Man defi­niert in einer Basisklasse den Zugriff auf die Session per AfterInterceptor (z.B. für einen Logger) und lei­tet diese dann in einem Controller ab. In einer neuen Action wird nun die Session ungül­tig gemacht und als Ergebnis löst der Aufruf der Session-Variable im Interceptor auto­ma­tisch eine IllegalStateException aus.

Dieser Aufruf kann auch schon die blosse Ausgabe der Session sein, da das Objekt im Controller nicht mehr exi­siert. Im nach­fol­gen­den Codeauszug wird der eben beschrie­bene Fall veranschaulicht:

class BasisController {
def afterInterceptor = {
//wirft eine IllegalStateException
println session
}

}

class UserInterface extends BasisController {
//Erzeugt nach dem ren­der eine IllegalStateException
def logout = {
session.invalidate()
render(view:'logout')
}
}

Ein Workaround wäre, im Interceptor nicht die Session-Variable des Controller zu nut­zen. Stattdessen sollte man die Session direkt aus dem Request holen:

class BasisController {
def afterInterceptor = {
//gibt die Session aus oder null, wenn diese ungül­tig wurde
println request.getSession(false)
}

}

Zugriffe ohne Session

Ein gro­ßer Nachteil der auto­ma­ti­schen Sessionvergabe war bis­lang auch die Tatsache, dass bei ein­zel­nen Controlleraufrufen (z.B. durch einen exter­nen Service), eben­falls eine Session erzeugt wurde, obwohl diese gar nicht benö­tigt wurde. So hatte man bei einem Aufruf durch einen Webservice nach jedem Request immer eine Session, wel­che unbe­nutzt auf den Timeout wartete.

Seit Grails 1.0.1 wird nur nach der expli­zi­ten Nutzung des Session Objektes im Controller eine neue Session ange­legt. Siehe dazu http://jira.codehaus.org/browse/GRAILS-1238.

Related Posts

Pin It on Pinterest