I’m attempting to secure a grails application using the shiro plugin. I have a simple authentication system in place that works fine with a simple app-run in development mode. However, once I run the application in production mode (grails prod run-app --stacktrace) Any attempt to login or register throws the error below and refuses to function:
| Error 2012-12-03 05:35:15,081 [http-bio-8080-exec-9] ERROR databasesession.GormPersisterService - [Assertion failed] - this String argument must have length; it must not be null or empty
Message: [Assertion failed] - this String argument must have length; it must not be null or empty
Line | Method
->> 45 | deleteBySessionId in grails.plugin.databasesession.PersistentSessionAttributeValue
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
| 111 | invalidate in grails.plugin.databasesession.GormPersisterService
| 90 | proxySession . . in grails.plugin.databasesession.SessionProxyFilter
| 42 | getSession in grails.plugin.databasesession.SessionProxyFilter$1
| 147 | getSession . . . in org.apache.shiro.web.servlet.ShiroHttpServletRequest
| 188 | getSession in ''
| 108 | createSession . . in org.apache.shiro.web.session.mgt.ServletContainerSessionManager
| 64 | start in ''
| 121 | start . . . . . . in org.apache.shiro.mgt.SessionsSecurityManager
| 336 | getSession in org.apache.shiro.subject.support.DelegatingSubject
| 314 | getSession . . . in ''
| 182 | mergePrincipals in org.apache.shiro.mgt.DefaultSubjectDAO
| 163 | saveToSession . . in ''
| 144 | save in ''
| 383 | save . . . . . . in org.apache.shiro.mgt.DefaultSecurityManager
| 350 | createSubject in ''
| 183 | createSubject . . in ''
| 283 | login in ''
| 257 | login . . . . . . in org.apache.shiro.subject.support.DelegatingSubject
| 68 | register in pfm.SignupController
| 195 | doFilter . . . . in grails.plugin.cache.web.filter.PageFragmentCachingFilter
| 63 | doFilter in grails.plugin.cache.web.filter.AbstractFilter
| 55 | doFilter . . . . in org.apache.shiro.grails.SavedRequestFilter
| 449 | executeChain in org.apache.shiro.web.servlet.AbstractShiroFilter
| 365 | call . . . . . . in org.apache.shiro.web.servlet.AbstractShiroFilter$1
| 90 | doCall in org.apache.shiro.subject.support.SubjectCallable
| 83 | call . . . . . . in ''
| 380 | execute in org.apache.shiro.subject.support.DelegatingSubject
| 362 | doFilterInternal in org.apache.shiro.web.servlet.AbstractShiroFilter
| 125 | doFilter in org.apache.shiro.web.servlet.OncePerRequestFilter
| 51 | doFilterInternal in grails.plugin.databasesession.SessionProxyFilter
| 886 | runTask in java.util.concurrent.ThreadPoolExecutor$Worker
| 908 | run . . . . . . . in ''
^ 680 | run in java.lang.Thread
Since database-session is disabled in development mode and the stacktrace contains databasesession I’m assuming that’s where the problem lies. I have no idea what’s causing it or how to fix it.
Some specs that might be helpful:
Grails 2.1.1
compile ":shiro:1.1.4"
Let me know if I can supply more information and thanks in advance
Update:
Here is the code that triggers it, in the auth controller:
def signIn = {
def authToken = new UsernamePasswordToken(params.username, params.password as String)
// Support for "remember me"
if (params.rememberMe) {
authToken.rememberMe = true
}
// If a controller redirected to this page, redirect back
// to it. Otherwise redirect to the root URI.
def targetUri = params.targetUri ?: "/"
// Handle requests saved by Shiro filters.
def savedRequest = WebUtils.getSavedRequest(request)
if (savedRequest) {
targetUri = savedRequest.requestURI - request.contextPath
if (savedRequest.queryString) targetUri = targetUri + '?' + savedRequest.queryString
}
try{
// Perform the actual login. An AuthenticationException
// will be thrown if the username is unrecognised or the
// password is incorrect.
SecurityUtils.subject.login(authToken)
log.info "Redirecting to '${targetUri}'."
redirect(uri: targetUri)
}
catch (AuthenticationException ex){
// Authentication failed, so display the appropriate message
// on the login page.
log.info "Authentication failure for user '${params.username}'."
flash.message = message(code: "login.failed")
// Keep the username and "remember me" setting so that the
// user doesn't have to enter them again.
def m = [ username: params.username ]
if (params.rememberMe) {
m["rememberMe"] = true
}
// Remember the target URI too.
if (params.targetUri) {
m["targetUri"] = params.targetUri
}
// Now redirect back to the login page.
redirect(action: "login", params: m)
}
}
The associated domain classes:
class User {
String username
String passwordHash
byte[] passwordSalt
Manager manager
static hasMany = [ roles: Role, permissions: String ]
static constraints = {
username(nullable: false, blank: false, unique: true)
manager(nullable: true)
}
}
And finally the security filter:
class SecurityFilters {
def publicActions = [
signup: ['index','register'],
auth:['*','*']
]
private boolean findAction(controllerName, actionName){
def c = publicActions[controllerName]
return(c)?c.find{(it==actionName||it=='*')}!=null:false
}
def filters = {
all(uri: "/**"){
before = {
//Check for public controller/actions
def isPublic=findAction(controllerName,actionName)
if(isPublic) return true
// Ignore direct views (e.g. the default main index page).
if (!controllerName) return true
accessControl()
}
}
}
}
hm. just took a look at the source of the database session plugin. I can’t match the linenumbers of your exception with the source. Which version of the plugin are you using?
For me, it looks like the database session plugin misses a sessionId and tries to invalidate the session with an invalid session id.
For me it looks like you’ve stumbled upon a bug in V1.12 of the plugin: http://jira.grails.org/browse/GPDATABASESESSION-1
This seems to be fixed already but not released on grails.org .
To get the latest version, download it from https://github.com/burtbeckwith/grails-database-session/archive/master.zip , unzip it and rename the directory to
grails-database-session.open a shell and cd into the
grails-database-sessiondirectory. Execute agrails package-plugin. If it complains about the wrong grails version, either switch to the right version or do agrails upgradeand agrails package-pluginagain.Now cd to your project and do a
grails install-plugin /path/to/grails-database-session/grails-database-session-1.2.zip.At least, this just worked for me…